Skip to content

Extensive refactor for typesafety - Mobile & RN-SDK need testing#15

Merged
djanogly merged 73 commits into
devfrom
pr/refactor-2
Mar 11, 2026
Merged

Extensive refactor for typesafety - Mobile & RN-SDK need testing#15
djanogly merged 73 commits into
devfrom
pr/refactor-2

Conversation

@djanogly
Copy link
Copy Markdown
Contributor

This pull request primarily refactors how Convex backend function references are used in the mobile app codebase, moving from direct usage of generated api references to explicit makeFunctionReference calls. This change addresses deep TypeScript instantiation issues and improves type safety and maintainability. Additionally, the CI workflow is enhanced to better summarize and control job outcomes, and the documentation and roadmap are updated for clarity and future planning.

Refactor: Convex Function References in Mobile App

Developer Experience & Documentation

  • Adds a section to AGENTS.md explaining the workaround for Convex TypeScript deep-instantiation errors, recommending the use of makeFunctionReference at problematic sites and keeping workarounds localized.

CI/CD Improvements

  • Updates .github/workflows/ci.yml to use new pnpm quality:lint and pnpm quality:typecheck scripts, adds continue-on-error for quality and security steps, and introduces a new step to summarize check results and fail the workflow if blocking steps fail.

Roadmap & Planning

  • Updates ROADMAP.md to clarify, reorganize, and expand on P0 tasks, including new items for deployment dependencies, AI features, mobile parity, and documentation structure. Also adds a section for RN & Native SDKs documentation topics. [1] [2]

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 11, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
opencom-landing Ready Ready Preview, Comment Mar 11, 2026 11:53am
opencom-web Ready Ready Preview, Comment Mar 11, 2026 11:53am

@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Modularize backend code and enhance E2E test infrastructure

✨ Enhancement 🧪 Tests

Grey Divider

Walkthroughs

Description
  * **Modularize large monolithic Convex backend files** into organized domain-specific submodules:
  - testData.ts: Extracted mutations into separate files (seeds, cleanup, demoWorkspace,
  landing)
  - series.ts: Split into authoring, runtime, and telemetry submodules
  - visitors.ts: Separated into coreQueries, directoryQueries, and mutations submodules
  - testing/helpers.ts: Reorganized into domain-specific helper modules (series, workspace,
  conversations, notifications, content, email, tickets, ai, cleanup)
  * **Add comprehensive E2E test data seed mutations** in new testData/seeds.ts with environment
  guards and test data prefixes for easy identification
  * **Improve E2E test reliability and maintainability**:
  - Extract ticket conversion logic into reusable helper with retry logic and error handling
  - Simplify public pages test by removing conditional setup logic
  - Simplify carousel deletion test with explicit button clicks instead of dialog auto-acceptance
  - Add explicit waits for UI element visibility
  * **Add widget helper function** for human agent name resolution with normalization and default
  fallback
  * **Remove unused test utilities** and clean up redundant auth state refresh calls
Diagram
flowchart LR
  A["Large Monolithic Files<br/>testData, series, visitors<br/>testing/helpers"] -->|"Extract & Reorganize"| B["Domain-Specific Submodules<br/>authoring, runtime, mutations<br/>coreQueries, directoryQueries"]
  C["E2E Tests<br/>public-pages, inbox<br/>carousels"] -->|"Extract Helpers<br/>Improve Reliability"| D["Reusable Test Utilities<br/>Explicit Waits<br/>Error Handling"]
  E["Test Data Seeds"] -->|"Add Environment Guards"| F["E2E Test Data<br/>with Prefixes"]
  B --> G["Improved Maintainability<br/>& Type Safety"]
  D --> G
  F --> G
Loading

Grey Divider

File Changes

1. packages/convex/convex/testData.ts Refactoring +22/-3354

Modularize test data mutations into separate files

• Refactored large monolithic test data file into modular structure by extracting mutations into
 separate files (seeds, cleanup, demoWorkspace, landing)
• Replaced 3300+ lines of inline mutation implementations with re-exports from imported modules
• Maintains all 24 exported mutations with identical signatures and functionality
• Improves code organization and maintainability without changing public API

packages/convex/convex/testData.ts


2. apps/web/e2e/public-pages.spec.ts 🧪 Tests +4/-33

Simplify public pages E2E test setup logic

• Removed conditional test setup logic that depended on TEST_ADMIN_SECRET environment variable
• Deleted calls to getPublicWorkspaceContext() and updateHelpCenterAccessPolicy() helper
 functions
• Removed unused imports for getPublicWorkspaceContext, updateHelpCenterAccessPolicy, and Id
 type
• Added clarifying comment explaining that public help center access is tested at backend policy
 level instead

apps/web/e2e/public-pages.spec.ts


3. apps/widget/src/components/conversationView/helpers.ts ✨ Enhancement +6/-0

Add human agent name resolution helper function

• Created new helper file with resolveHumanAgentName() function
• Function normalizes sender names and returns default agent name if input is empty or whitespace
• Imports DEFAULT_HUMAN_AGENT_NAME constant from constants module

apps/widget/src/components/conversationView/helpers.ts


View more (107)
4. packages/convex/convex/testing/helpers.ts Refactoring +93/-2618

Modularize test helpers into organized domain-specific files

• Refactored massive 2600+ line file into modular helper imports from separate files
• Removed all inline test helper implementations and replaced with re-exports from organized helper
 modules
• Imports now organized by domain: series, workspace, conversations, notifications,
 content, email, tickets, ai, and cleanup
• All exported functions maintain same signatures and behavior through re-export pattern

packages/convex/convex/testing/helpers.ts


5. packages/convex/convex/testData/seeds.ts 🧪 Tests +682/-0

Add comprehensive E2E test data seed mutations

• New file containing seed data mutations for E2E testing with ALLOW_TEST_DATA environment guard
• Implements seedTour, seedSurvey, seedCarousel, seedOutboundMessage, seedArticles,
 seedVisitor, seedSegment, seedMessengerSettings, and seedAIAgentSettings mutations
• Each seed function generates test data with e2e_test_ prefix for easy identification and cleanup
• Exported as seedMutations object containing all seed functions

packages/convex/convex/testData/seeds.ts


6. apps/web/e2e/inbox.spec.ts 🧪 Tests +33/-6

Extract ticket conversion helper and improve test reliability

• Extracted ticket conversion logic into new convertConversationToTicket helper function with
 retry logic
• Helper includes error handling for workflow errors and navigation verification with timeout
 management
• Updated test to use new helper function instead of inline conversion steps
• Added explicit wait for suggestions sidecar visibility before interaction in suggestions test

apps/web/e2e/inbox.spec.ts


7. packages/convex/convex/series.ts Refactoring +28/-2429

Modularize series.ts into separate submodules

• Refactored large monolithic file into modular exports from three separate submodules
• Removed 2400+ lines of implementation code (validators, helpers, mutations, queries)
• Now re-exports authoring functions from ./series/authoring
• Now re-exports runtime functions from ./series/runtime
• Now re-exports telemetry functions from ./series/telemetry

packages/convex/convex/series.ts


8. packages/convex/convex/visitors.ts Refactoring +3/-1067

Modularize visitors.ts into separate submodules

• Removed 1000+ lines of implementation code including validators, helpers, and query/mutation
 handlers
• Refactored to re-export core query functions from ./visitors/coreQueries
• Refactored to re-export directory query functions from ./visitors/directoryQueries
• Refactored to re-export mutation functions from ./visitors/mutations

packages/convex/convex/visitors.ts


9. apps/web/e2e/carousels.spec.ts 🧪 Tests +1/-4

Simplify carousel deletion test and remove unused auth refresh

• Removed unused refreshAuthState import from auth-refresh helpers
• Removed redundant auth state refresh call and assertion in ensureAuthenticated function
• Changed dialog handling for carousel deletion from page.once("dialog") to explicit button click
• Updated delete confirmation to use getByRole("button", { name: /^confirm$/i }) instead of
 auto-accepting dialog

apps/web/e2e/carousels.spec.ts


10. .github/workflows/ci.yml Additional files +67/-5

...

.github/workflows/ci.yml


11. AGENTS.md Additional files +9/-0

...

AGENTS.md


12. ROADMAP.md Additional files +71/-6

...

ROADMAP.md


13. apps/mobile/app/(app)/conversation/[id].tsx Additional files +61/-10

...

apps/mobile/app/(app)/conversation/[id].tsx


14. apps/mobile/app/(app)/index.tsx Additional files +22/-4

...

apps/mobile/app/(app)/index.tsx


15. apps/mobile/app/(app)/onboarding.tsx Additional files +64/-8

...

apps/mobile/app/(app)/onboarding.tsx


16. apps/mobile/app/(app)/settings.tsx Additional files +98/-15

...

apps/mobile/app/(app)/settings.tsx


17. apps/mobile/package.json Additional files +1/-1

...

apps/mobile/package.json


18. apps/mobile/src/contexts/AuthContext.tsx Additional files +45/-7

...

apps/mobile/src/contexts/AuthContext.tsx


19. apps/mobile/src/contexts/NotificationContext.tsx Additional files +15/-3

...

apps/mobile/src/contexts/NotificationContext.tsx


20. apps/web/e2e/SKIP_REGISTRY.md Additional files +0/-2

...

apps/web/e2e/SKIP_REGISTRY.md


21. apps/web/e2e/ai-agent-settings.spec.ts Additional files +21/-18

...

apps/web/e2e/ai-agent-settings.spec.ts


22. apps/web/e2e/auth.spec.ts Additional files +5/-3

...

apps/web/e2e/auth.spec.ts


23. apps/web/e2e/csat.spec.ts Additional files +19/-12

...

apps/web/e2e/csat.spec.ts


24. apps/web/e2e/fixtures.ts Additional files +12/-1

...

apps/web/e2e/fixtures.ts


25. apps/web/e2e/helpers/auth-refresh.ts Additional files +238/-38

...

apps/web/e2e/helpers/auth-refresh.ts


26. apps/web/e2e/helpers/storage-state.ts Additional files +97/-0

...

apps/web/e2e/helpers/storage-state.ts


27. apps/web/e2e/helpers/widget-helpers.ts Additional files +84/-13

...

apps/web/e2e/helpers/widget-helpers.ts


28. apps/web/e2e/home-settings.spec.ts Additional files +63/-97

...

apps/web/e2e/home-settings.spec.ts


29. apps/web/e2e/knowledge.spec.ts Additional files +50/-320

...

apps/web/e2e/knowledge.spec.ts


30. apps/web/e2e/outbound.spec.ts Additional files +8/-6

...

apps/web/e2e/outbound.spec.ts


31. apps/web/e2e/reports.spec.ts Additional files +1/-12

...

apps/web/e2e/reports.spec.ts


32. apps/web/e2e/snippets.spec.ts Additional files +1/-3

...

apps/web/e2e/snippets.spec.ts


33. apps/web/e2e/tooltips.spec.ts Additional files +30/-4

...

apps/web/e2e/tooltips.spec.ts


34. apps/web/e2e/widget-features.spec.ts Additional files +144/-77

...

apps/web/e2e/widget-features.spec.ts


35. apps/web/e2e/widget.spec.ts Additional files +19/-8

...

apps/web/e2e/widget.spec.ts


36. apps/web/next.config.js Additional files +1/-1

...

apps/web/next.config.js


37. apps/web/package.json Additional files +3/-3

...

apps/web/package.json


38. apps/web/src/app/articles/ArticlesImportSection.tsx Additional files +355/-0

...

apps/web/src/app/articles/ArticlesImportSection.tsx


39. apps/web/src/app/articles/ArticlesListSection.tsx Additional files +263/-0

...

apps/web/src/app/articles/ArticlesListSection.tsx


40. apps/web/src/app/articles/DeleteArticleDialog.tsx Additional files +49/-0

...

apps/web/src/app/articles/DeleteArticleDialog.tsx


41. apps/web/src/app/articles/[id]/page.test.tsx Additional files +298/-0

...

apps/web/src/app/articles/[id]/page.test.tsx


42. apps/web/src/app/articles/[id]/page.tsx Additional files +138/-46

...

apps/web/src/app/articles/[id]/page.tsx


43. apps/web/src/app/articles/articlesAdminTypes.ts Additional files +113/-0

...

apps/web/src/app/articles/articlesAdminTypes.ts


44. apps/web/src/app/articles/articlesAdminUtils.test.ts Additional files +85/-0

...

apps/web/src/app/articles/articlesAdminUtils.test.ts


45. apps/web/src/app/articles/articlesAdminUtils.ts Additional files +181/-0

...

apps/web/src/app/articles/articlesAdminUtils.ts


46. apps/web/src/app/articles/collections/page.tsx Additional files +8/-14

...

apps/web/src/app/articles/collections/page.tsx


47. apps/web/src/app/articles/hooks/useArticleCollectionsConvex.ts Additional files +60/-0

...

apps/web/src/app/articles/hooks/useArticleCollectionsConvex.ts


48. apps/web/src/app/articles/hooks/useArticleEditorConvex.ts Additional files +120/-0

...

apps/web/src/app/articles/hooks/useArticleEditorConvex.ts


49. apps/web/src/app/articles/hooks/useArticlesAdminConvex.ts Additional files +158/-0

...

apps/web/src/app/articles/hooks/useArticlesAdminConvex.ts


50. apps/web/src/app/articles/page.tsx Additional files +174/-744

...

apps/web/src/app/articles/page.tsx


51. apps/web/src/app/audit-logs/page.tsx Additional files +1/-1

...

apps/web/src/app/audit-logs/page.tsx


52. apps/web/src/app/campaigns/carousels/[id]/page.tsx Additional files +15/-29

...

apps/web/src/app/campaigns/carousels/[id]/page.tsx


53. apps/web/src/app/campaigns/email/[id]/page.tsx Additional files +84/-12

...

apps/web/src/app/campaigns/email/[id]/page.tsx


54. apps/web/src/app/campaigns/hooks/useCampaignsPageConvex.ts Additional files +156/-0

...

apps/web/src/app/campaigns/hooks/useCampaignsPageConvex.ts


55. apps/web/src/app/campaigns/hooks/useCarouselEditorConvex.ts Additional files +89/-0

...

apps/web/src/app/campaigns/hooks/useCarouselEditorConvex.ts


56. apps/web/src/app/campaigns/hooks/usePushCampaignEditorConvex.ts Additional files +77/-0

...

apps/web/src/app/campaigns/hooks/usePushCampaignEditorConvex.ts


57. apps/web/src/app/campaigns/hooks/useSeriesEditorConvex.ts Additional files +105/-0

...

apps/web/src/app/campaigns/hooks/useSeriesEditorConvex.ts


58. apps/web/src/app/campaigns/page.tsx Additional files +22/-41

...

apps/web/src/app/campaigns/page.tsx


59. apps/web/src/app/campaigns/push/[id]/page.tsx Additional files +11/-16

...

apps/web/src/app/campaigns/push/[id]/page.tsx


60. apps/web/src/app/campaigns/series/[id]/SeriesEditorCanvas.tsx Additional files +127/-0

...

apps/web/src/app/campaigns/series/[id]/SeriesEditorCanvas.tsx


61. apps/web/src/app/campaigns/series/[id]/SeriesEditorInspector.tsx Additional files +319/-0

...

apps/web/src/app/campaigns/series/[id]/SeriesEditorInspector.tsx


62. apps/web/src/app/campaigns/series/[id]/SeriesEditorSidebar.tsx Additional files +314/-0

...

apps/web/src/app/campaigns/series/[id]/SeriesEditorSidebar.tsx


63. apps/web/src/app/campaigns/series/[id]/page.tsx Additional files +109/-870

...

apps/web/src/app/campaigns/series/[id]/page.tsx


64. apps/web/src/app/campaigns/series/[id]/seriesBlockUi.tsx Additional files +57/-0

...

apps/web/src/app/campaigns/series/[id]/seriesBlockUi.tsx


65. apps/web/src/app/campaigns/series/[id]/seriesEditorTypes.ts Additional files +138/-0

...

apps/web/src/app/campaigns/series/[id]/seriesEditorTypes.ts


66. apps/web/src/app/checklists/[id]/page.tsx Additional files +8/-43

...

apps/web/src/app/checklists/[id]/page.tsx


67. apps/web/src/app/checklists/checklistTypes.ts Additional files +46/-0

...

apps/web/src/app/checklists/checklistTypes.ts


68. apps/web/src/app/checklists/hooks/useChecklistBuilderConvex.ts Additional files +61/-0

...

apps/web/src/app/checklists/hooks/useChecklistBuilderConvex.ts


69. apps/web/src/app/checklists/hooks/useChecklistsPageConvex.ts Additional files +64/-0

...

apps/web/src/app/checklists/hooks/useChecklistsPageConvex.ts


70. apps/web/src/app/checklists/page.tsx Additional files +7/-18

...

apps/web/src/app/checklists/page.tsx


71. apps/web/src/app/help/[slug]/page.tsx Additional files +51/-9

...

apps/web/src/app/help/[slug]/page.tsx


72. apps/web/src/app/help/helpCenterLinks.ts Additional files +72/-0

...

apps/web/src/app/help/helpCenterLinks.ts


73. apps/web/src/app/help/page.tsx Additional files +45/-8

...

apps/web/src/app/help/page.tsx


74. apps/web/src/app/inbox/InboxAiReviewPanel.tsx Additional files +208/-0

...

apps/web/src/app/inbox/InboxAiReviewPanel.tsx


75. apps/web/src/app/inbox/InboxConversationListPane.tsx Additional files +191/-0

...

apps/web/src/app/inbox/InboxConversationListPane.tsx


76. apps/web/src/app/inbox/InboxThreadPane.test.tsx Additional files +192/-0

...

apps/web/src/app/inbox/InboxThreadPane.test.tsx


77. apps/web/src/app/inbox/InboxThreadPane.tsx Additional files +622/-0

...

apps/web/src/app/inbox/InboxThreadPane.tsx


78. apps/web/src/app/inbox/README.md Additional files +43/-0

...

apps/web/src/app/inbox/README.md


79. apps/web/src/app/inbox/hooks/useInboxAttentionCues.test.tsx Additional files +124/-0

...

apps/web/src/app/inbox/hooks/useInboxAttentionCues.test.tsx


80. apps/web/src/app/inbox/hooks/useInboxAttentionCues.ts Additional files +172/-0

...

apps/web/src/app/inbox/hooks/useInboxAttentionCues.ts


81. apps/web/src/app/inbox/hooks/useInboxCompactPanels.test.tsx Additional files +123/-0

...

apps/web/src/app/inbox/hooks/useInboxCompactPanels.test.tsx


82. apps/web/src/app/inbox/hooks/useInboxCompactPanels.ts Additional files +90/-0

...

apps/web/src/app/inbox/hooks/useInboxCompactPanels.ts


83. apps/web/src/app/inbox/hooks/useInboxConvex.ts Additional files +192/-0

...

apps/web/src/app/inbox/hooks/useInboxConvex.ts


84. apps/web/src/app/inbox/hooks/useInboxMessageActions.ts Additional files +215/-0

...

apps/web/src/app/inbox/hooks/useInboxMessageActions.ts


85. apps/web/src/app/inbox/hooks/useInboxSelectionSync.test.tsx Additional files +143/-0

...

apps/web/src/app/inbox/hooks/useInboxSelectionSync.test.tsx


86. apps/web/src/app/inbox/hooks/useInboxSelectionSync.ts Additional files +147/-0

...

apps/web/src/app/inbox/hooks/useInboxSelectionSync.ts


87. apps/web/src/app/inbox/hooks/useInboxSuggestionsCount.ts Additional files +73/-0

...

apps/web/src/app/inbox/hooks/useInboxSuggestionsCount.ts


88. apps/web/src/app/inbox/inboxRenderTypes.ts Additional files +103/-0

...

apps/web/src/app/inbox/inboxRenderTypes.ts


89. apps/web/src/app/inbox/page.tsx Additional files +360/-1427

...

apps/web/src/app/inbox/page.tsx


90. apps/web/src/app/knowledge/internal/[id]/page.tsx Additional files +9/-307

...

apps/web/src/app/knowledge/internal/[id]/page.tsx


91. apps/web/src/app/knowledge/internal/new/page.tsx Additional files +3/-207

...

apps/web/src/app/knowledge/internal/new/page.tsx


92. apps/web/src/app/knowledge/page.tsx Additional files +2/-699

...

apps/web/src/app/knowledge/page.tsx


93. apps/web/src/app/onboarding/page.tsx Additional files +56/-6

...

apps/web/src/app/onboarding/page.tsx


94. apps/web/src/app/outbound/[id]/OutboundClickActionPanel.tsx Additional files +128/-0

...

apps/web/src/app/outbound/[id]/OutboundClickActionPanel.tsx


95. apps/web/src/app/outbound/[id]/OutboundContentEditor.tsx Additional files +290/-0

...

apps/web/src/app/outbound/[id]/OutboundContentEditor.tsx


96. apps/web/src/app/outbound/[id]/OutboundEditorHeader.tsx Additional files +73/-0

...

apps/web/src/app/outbound/[id]/OutboundEditorHeader.tsx


97. apps/web/src/app/outbound/[id]/OutboundFieldLabel.tsx Additional files +16/-0

...

apps/web/src/app/outbound/[id]/OutboundFieldLabel.tsx


98. apps/web/src/app/outbound/[id]/OutboundFrequencyPanel.tsx Additional files +25/-0

...

apps/web/src/app/outbound/[id]/OutboundFrequencyPanel.tsx


99. apps/web/src/app/outbound/[id]/OutboundPreviewPanel.tsx Additional files +102/-0

...

apps/web/src/app/outbound/[id]/OutboundPreviewPanel.tsx


100. apps/web/src/app/outbound/[id]/OutboundStatisticsPanel.tsx Additional files +43/-0

...

apps/web/src/app/outbound/[id]/OutboundStatisticsPanel.tsx


101. apps/web/src/app/outbound/[id]/OutboundTriggerPanel.tsx Additional files +99/-0

...

apps/web/src/app/outbound/[id]/OutboundTriggerPanel.tsx


102. apps/web/src/app/outbound/[id]/editorState.test.ts Additional files +59/-0

...

apps/web/src/app/outbound/[id]/editorState.test.ts


103. apps/web/src/app/outbound/[id]/editorState.ts Additional files +225/-0

...

apps/web/src/app/outbound/[id]/editorState.ts


104. apps/web/src/app/outbound/[id]/page.tsx Additional files +74/-861

...

apps/web/src/app/outbound/[id]/page.tsx


105. apps/web/src/app/outbound/hooks/useOutboundMessageEditorController.ts Additional files +136/-0

...

apps/web/src/app/outbound/hooks/useOutboundMessageEditorController.ts


106. apps/web/src/app/outbound/hooks/useOutboundMessageEditorConvex.ts Additional files +122/-0

...

apps/web/src/app/outbound/hooks/useOutboundMessageEditorConvex.ts


107. apps/web/src/app/outbound/outboundMessageUi.tsx Additional files +47/-0

...

apps/web/src/app/outbound/outboundMessageUi.tsx


108. apps/web/src/app/outbound/page.tsx Additional files +92/-56

...

apps/web/src/app/outbound/page.tsx


109. apps/web/src/app/reports/ai/page.tsx Additional files +62/-5

...

apps/web/src/app/reports/ai/page.tsx


110. Additional files not shown Additional files +0/-0

...

Additional files not shown


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented Mar 11, 2026

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Any-args gate ignored 🐞 Bug ⛨ Security
Description
In .github/workflows/ci.yml, the summarizer reports the pnpm security:convex-any-args-gate step
using report_warning, and report_warning never sets the failures flag, so the workflow can
succeed even when the v.any() security gate fails. This disables a documented CI security gate
intended to block unsafe Convex argument validators from merging.
Code

.github/workflows/ci.yml[112]

+          report_warning "Convex validator any guard" "${{ steps.convex_any_guard.outcome }}"
Evidence
report_warning only emits a warning and does not set failures=1, and the workflow only exits
non-zero when failures != 0; since the any-args gate is fed into report_warning, its failure
will not fail the job. Documentation explicitly lists pnpm security:convex-any-args-gate as a CI
security gate in the Checks job.

.github/workflows/ci.yml[97-121]
docs/testing.md[236-246]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The CI workflow currently treats the Convex `v.any()` argument gate (`pnpm security:convex-any-args-gate`) as warning-only. Because `report_warning` never sets the `failures` flag, the workflow can exit successfully even when this security gate fails.

## Issue Context
This gate is documented as part of the Checks job security gates, so it should be enforced as blocking.

## Fix Focus Areas
- .github/workflows/ci.yml[97-121]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: checks

Failed stage: Summarize check results [❌]

Failed test name: ""

Failure summary:

The action failed due to a blocking security/dependency audit gate failure:
- node
scripts/ci-audit-gate.js reported a high severity advisory for tar (id=1114302) “node-tar
Symlink Path Traversal via Drive-Relative Linkpath” (GHSA-9ppj-qmqm-q256) that is not
allowlisted
, and exited with code 1 (log lines 638-642).
- The final gate summary marks Dependency
audit gate as failure and exits the job with status 1 (log lines 679-683, 696-697).

Notes from the log that did not fail the job:
- ESLint reported warnings only (0 errors, ...
warnings) for web and convex lint (lines 207-217, 377-392).
- security:convex-any-args-gate detected
stale exception entries and exited 1, but the workflow treats this as “warning only” (lines 425-437,
691).
- pnpm test:convex tests completed successfully; the “AI generation error” messages are stderr
logs, not test failures (lines 468-505).

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

202:  > pnpm web:lint && pnpm convex:lint
203:  > [email protected] web:lint /home/runner/work/opencom/opencom
204:  > pnpm --filter @opencom/web lint
205:  > @opencom/[email protected] lint /home/runner/work/opencom/opencom/apps/web
206:  > eslint src --ext .ts,.tsx
207:  /home/runner/work/opencom/opencom/apps/web/src/app/login/page.test.tsx
208:  ##[warning]  45:42  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
209:  /home/runner/work/opencom/opencom/apps/web/src/app/settings/MessengerSettingsSection.test.tsx
210:  ##[warning]  24:61  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
211:  /home/runner/work/opencom/opencom/apps/web/src/app/signup/page.test.tsx
212:  ##[warning]  42:42  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
213:  /home/runner/work/opencom/opencom/apps/web/src/components/ResponsiveLayout.tsx
214:  ##[warning]  152:6  warning  React Hook useEffect has a missing dependency: 'closePanel'. Either include it or remove the dependency array  react-hooks/exhaustive-deps
215:  /home/runner/work/opencom/opencom/apps/web/src/contexts/BackendContext.tsx
216:  ##[warning]  86:6  warning  React Hook useEffect has a missing dependency: 'loadBackendStorage'. Either include it or remove the dependency array  react-hooks/exhaustive-deps
217:  ✖ 5 problems (0 errors, 5 warnings)
218:  > [email protected] convex:lint /home/runner/work/opencom/opencom
...

377:  ##[warning]  90:12  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
378:  /home/runner/work/opencom/opencom/packages/convex/tests/visitorDirectoryAuthorizationSemantics.test.ts
379:  ##[warning]   62:81  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
380:  ##[warning]   74:10  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
381:  ##[warning]   77:81  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
382:  ##[warning]   91:10  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
383:  ##[warning]  116:13  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
384:  ##[warning]  138:10  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
385:  ##[warning]  165:13  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
386:  ##[warning]  187:10  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
387:  ##[warning]  200:65  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
388:  ##[warning]  215:10  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
389:  ##[warning]  233:65  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
390:  ##[warning]  254:10  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
391:  ##[warning]  348:65  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
392:  ✖ 146 problems (0 errors, 146 warnings)
393:  ##[group]Run pnpm quality:typecheck
...

421:  shell: /usr/bin/bash -e {0}
422:  env:
423:  PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin
424:  ##[endgroup]
425:  > [email protected] security:convex-any-args-gate /home/runner/work/opencom/opencom
426:  > node scripts/ci-convex-any-args-gate.js
427:  [convex-any-args-gate] Violations detected:
428:  - Stale exception entry with no current v.any() usage: packages/convex/convex/testData.ts:seedVisitor
429:  - Stale exception entry with no current v.any() usage: packages/convex/convex/testData.ts:seedSegment
430:  - Stale exception entry with no current v.any() usage: packages/convex/convex/testing/helpers.ts:createTestVisitor
431:  - Stale exception entry with no current v.any() usage: packages/convex/convex/testing/helpers.ts:updateTestArticle
432:  - Stale exception entry with no current v.any() usage: packages/convex/convex/testing/helpers.ts:createTestTour
433:  - Stale exception entry with no current v.any() usage: packages/convex/convex/testing/helpers.ts:updateTestTour
434:  - Stale exception entry with no current v.any() usage: packages/convex/convex/testing/helpers.ts:createTestAuditLog
435:  - Stale exception entry with no current v.any() usage: packages/convex/convex/testing/helpers.ts:createTestPushCampaign
436:  ELIFECYCLE  Command failed with exit code 1.
437:  ##[error]Process completed with exit code 1.
438:  ##[group]Run pnpm security:secret-scan
...

455:  > [email protected] security:headers-check /home/runner/work/opencom/opencom
456:  > node scripts/ci-security-headers-check.js
457:  [security-headers-check] OK: web and landing header requirements validated.
458:  ##[group]Run pnpm test:convex
459:  �[36;1mpnpm test:convex�[0m
460:  shell: /usr/bin/bash -e {0}
461:  env:
462:  PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin
463:  ##[endgroup]
464:  > [email protected] test:convex /home/runner/work/opencom/opencom
465:  > pnpm --filter @opencom/convex test
466:  > @opencom/[email protected] test /home/runner/work/opencom/opencom/packages/convex
467:  > vitest run
468:  �[1m�[46m RUN �[49m�[22m �[36mv4.0.17 �[39m�[90m/home/runner/work/opencom/opencom/packages/convex�[39m
469:  �[32m✓�[39m tests/runtimeTypeHardeningGuard.test.ts �[2m(�[22m�[2m21 tests�[22m�[2m)�[22m�[32m 23�[2mms�[22m�[39m
470:  �[90mstderr�[2m | tests/aiAgentRuntimeSafety.test.ts�[2m > �[22m�[2maiAgentActions runtime safety�[2m > �[22m�[2mpersists a handoff message when generation fails
471:  �[22m�[39mAI generation error: Error: gateway timeout
472:  at �[90m/home/runner/work/opencom/opencom/packages/convex/�[39mtests/aiAgentRuntimeSafety.test.ts:592:40
473:  at file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/runner�[24m/dist/index.js:145:11
474:  at file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/runner�[24m/dist/index.js:915:26
475:  at file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/runner�[24m/dist/index.js:1243:20
476:  at new Promise (<anonymous>)
477:  at runWithTimeout (file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/runner�[24m/dist/index.js:1209:10)
478:  at file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/runner�[24m/dist/index.js:1653:37
479:  at Traces.$ (file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/[email protected]_@[email protected]_@[email protected]_@[email protected][email protected]_jsd_znh4lc65ld7o7n72tbcfnzmloy/node_modules/�[4mvitest�[24m/dist/chunks/traces.CCmnQaNT.js:142:27)
480:  at trace (file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/[email protected]_@[email protected]_@[email protected]_@[email protected][email protected]_jsd_znh4lc65ld7o7n72tbcfnzmloy/node_modules/�[4mvitest�[24m/dist/chunks/test.B8ej_ZHS.js:239:21)
481:  at runTest (file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/runner�[24m/dist/index.js:1653:12)
482:  �[90mstderr�[2m | tests/aiAgentRuntimeSafety.test.ts�[2m > �[22m�[2maiAgentActions runtime safety�[2m > �[22m�[2mfalls back to a persisted bot message if handoff fails after generation error
483:  �[22m�[39mAI generation error: Error: provider unavailable
484:  at �[90m/home/runner/work/opencom/opencom/packages/convex/�[39mtests/aiAgentRuntimeSafety.test.ts:670:40
485:  at file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/runner�[24m/dist/index.js:145:11
486:  at file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/runner�[24m/dist/index.js:915:26
487:  at file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/runner�[24m/dist/index.js:1243:20
488:  at new Promise (<anonymous>)
489:  at runWithTimeout (file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/runner�[24m/dist/index.js:1209:10)
490:  at file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/runner�[24m/dist/index.js:1653:37
491:  at Traces.$ (file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/[email protected]_@[email protected]_@[email protected]_@[email protected][email protected]_jsd_znh4lc65ld7o7n72tbcfnzmloy/node_modules/�[4mvitest�[24m/dist/chunks/traces.CCmnQaNT.js:142:27)
492:  at trace (file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/[email protected]_@[email protected]_@[email protected]_@[email protected][email protected]_jsd_znh4lc65ld7o7n72tbcfnzmloy/node_modules/�[4mvitest�[24m/dist/chunks/test.B8ej_ZHS.js:239:21)
493:  at runTest (file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/runner�[24m/dist/index.js:1653:12)
494:  �[90mstderr�[2m | tests/aiAgentRuntimeSafety.test.ts�[2m > �[22m�[2maiAgentActions runtime safety�[2m > �[22m�[2mfalls back to a persisted bot message if handoff fails after generation error
495:  �[22m�[39mFailed to handoff after AI generation error: Error: handoff unavailable
496:  at �[90m/home/runner/work/opencom/opencom/packages/convex/�[39mtests/aiAgentRuntimeSafety.test.ts:700:15
497:  at Mock (file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/spy�[24m/dist/index.js:285:34)
498:  at handleGenerationFailure �[90m(/home/runner/work/opencom/opencom/packages/convex/�[39mconvex/aiAgentActions.ts:454:32�[90m)�[39m
499:  �[90m    at processTicksAndRejections (node:internal/process/task_queues:95:5)�[39m
500:  at Function.handler [as _handler] �[90m(/home/runner/work/opencom/opencom/packages/convex/�[39mconvex/aiAgentActions.ts:625:14�[90m)�[39m
501:  at �[90m/home/runner/work/opencom/opencom/packages/convex/�[39mtests/aiAgentRuntimeSafety.test.ts:711:20
502:  at file:///home/runner/work/opencom/opencom/node_modules/�[4m.pnpm�[24m/@[email protected]/node_modules/�[4m@vitest/runner�[24m/dist/index.js:915:20
503:  �[32m✓�[39m tests/aiAgentRuntimeSafety.test.ts �[2m(�[22m�[2m14 tests�[22m�[2m)�[22m�[32m 49�[2mms�[22m�[39m
504:  �[32m✓�[39m tests/reportingCsatEligibilitySemantics.test.ts �[2m(�[22m�[2m6 tests�[22m�[2m)�[22m�[32m 15�[2mms�[22m�[39m
505:  �[32m✓�[39m tests/visitorDirectoryAuthorizationSemantics.test.ts �[2m(�[22m�[2m7 tests�[22m�[2m)�[22m�[32m 14�[2mms�[22m�[39m
506:  �[90mstderr�[2m | tests/notificationRouting.test.ts�[2m > �[22m�[2mnotification routing�[2m > �[22m�[2mremoves invalid agent and visitor tokens after transport errors
507:  �[22m�[39m[Push] Failed to send to ExponentPushToken[agent-invalid]: DeviceNotRegistered: The device is not registered
508:  [Push] Failed to send to ExponentPushToken[visitor-invalid]: DeviceNotRegistered: The device is not registered
509:  �[32m✓�[39m tests/auditLogs.test.ts �[2m(�[22m�[2m17 tests�[22m�[2m)�[22m�[32m 13�[2mms�[22m�[39m
...

627:  ├ chunks/186ceb5a-cd350ebda3c37bce.js  54.2 kB
628:  ├ chunks/5470-86efb4adbf0b2de3.js      45.8 kB
629:  └ other shared chunks (total)          1.93 kB
630:  ○  (Static)   prerendered as static content
631:  ƒ  (Dynamic)  server-rendered on demand
632:  ##[group]Run node scripts/ci-audit-gate.js
633:  �[36;1mnode scripts/ci-audit-gate.js�[0m
634:  shell: /usr/bin/bash -e {0}
635:  env:
636:  PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin
637:  ##[endgroup]
638:  [audit-gate] Blocking advisories detected:
639:  - [high] tar (id=1114302): node-tar Symlink Path Traversal via Drive-Relative Linkpath
640:  reason: not allowlisted
641:  advisory: https://github.com/advisories/GHSA-9ppj-qmqm-q256
642:  ##[error]Process completed with exit code 1.
643:  ##[group]Run failures=0
644:  �[36;1mfailures=0�[0m
645:  �[36;1m�[0m
646:  �[36;1mreport_blocking() {�[0m
647:  �[36;1m  name="$1"�[0m
648:  �[36;1m  outcome="$2"�[0m
649:  �[36;1m  if [ "$outcome" = "success" ]; then�[0m
650:  �[36;1m    echo "::notice::$name passed"�[0m
651:  �[36;1m  elif [ "$outcome" = "skipped" ]; then�[0m
652:  �[36;1m    echo "::warning::$name skipped"�[0m
653:  �[36;1m  else�[0m
654:  �[36;1m    echo "::error::$name failed"�[0m
655:  �[36;1m    failures=1�[0m
656:  �[36;1m  fi�[0m
657:  �[36;1m}�[0m
658:  �[36;1m�[0m
659:  �[36;1mreport_warning() {�[0m
660:  �[36;1m  name="$1"�[0m
661:  �[36;1m  outcome="$2"�[0m
662:  �[36;1m  if [ "$outcome" = "success" ]; then�[0m
663:  �[36;1m    echo "::notice::$name passed"�[0m
664:  �[36;1m  elif [ "$outcome" = "skipped" ]; then�[0m
665:  �[36;1m    echo "::warning::$name skipped"�[0m
666:  �[36;1m  else�[0m
667:  �[36;1m    echo "::warning::$name failed (warning only)"�[0m
668:  �[36;1m  fi�[0m
...

676:  �[36;1mreport_blocking "Security headers policy check" "success"�[0m
677:  �[36;1mreport_blocking "Convex backend tests" "success"�[0m
678:  �[36;1mreport_blocking "Web production build" "success"�[0m
679:  �[36;1mreport_blocking "Dependency audit gate" "failure"�[0m
680:  �[36;1m�[0m
681:  �[36;1mif [ "$failures" -ne 0 ]; then�[0m
682:  �[36;1m  exit 1�[0m
683:  �[36;1mfi�[0m
684:  shell: /usr/bin/bash -e {0}
685:  env:
686:  PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin
687:  ##[endgroup]
688:  ##[notice]Lint (web + convex) passed
689:  ##[notice]Typecheck (web + convex) passed
690:  ##[notice]Convex raw auth guard passed
691:  ##[warning]Convex validator any guard failed (warning only)
692:  ##[notice]Secret scan gate passed
693:  ##[notice]Security headers policy check passed
694:  ##[notice]Convex backend tests passed
695:  ##[notice]Web production build passed
696:  ##[error]Dependency audit gate failed
697:  ##[error]Process completed with exit code 1.
698:  Post job cleanup.

Comment thread .github/workflows/ci.yml
report_blocking "Lint (web + convex)" "${{ steps.lint.outcome }}"
report_blocking "Typecheck (web + convex)" "${{ 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 }}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Any-args gate ignored 🐞 Bug ⛨ Security

In .github/workflows/ci.yml, the summarizer reports the pnpm security:convex-any-args-gate step
using report_warning, and report_warning never sets the failures flag, so the workflow can
succeed even when the v.any() security gate fails. This disables a documented CI security gate
intended to block unsafe Convex argument validators from merging.
Agent Prompt
## Issue description
The CI workflow currently treats the Convex `v.any()` argument gate (`pnpm security:convex-any-args-gate`) as warning-only. Because `report_warning` never sets the `failures` flag, the workflow can exit successfully even when this security gate fails.

## Issue Context
This gate is documented as part of the Checks job security gates, so it should be enforced as blocking.

## Fix Focus Areas
- .github/workflows/ci.yml[97-121]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@djanogly djanogly requested review from Copilot March 11, 2026 11:53
@djanogly djanogly merged commit 286ed94 into dev Mar 11, 2026
4 of 5 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors Convex function reference usage (moving from generated api.* property access to explicit makeFunctionReference/web*Ref patterns) to mitigate deep TypeScript instantiation issues, while also expanding web admin features (inbox/outbound/articles), improving E2E stability, and enhancing CI reporting/docs.

Changes:

  • Replaces direct generated Convex api.* usage with explicit function references across web + mobile, and adds several web “Convex hook” wrappers (useWebQuery/useWebMutation/...).
  • Adds new web admin modules (inbox orchestration hooks, outbound editor hooks/UI, articles admin utils/hooks) with accompanying unit tests.
  • Improves CI workflow result summarization and Playwright E2E robustness (selectors, auth/storage-state handling), plus roadmap/agent docs updates.

Reviewed changes

Copilot reviewed 118 out of 919 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
apps/web/src/app/settings/MessengerSettingsSection.test.tsx Adds regression tests for messenger settings error feedback.
apps/web/src/app/settings/MessengerSettingsPreview.tsx Introduces a live preview component for messenger settings form state.
apps/web/src/app/settings/HelpCenterAccessSection.tsx Adds UI to toggle help center access policy.
apps/web/src/app/settings/EmailChannelSection.tsx Adds UI to configure the email support channel settings.
apps/web/src/app/settings/AutomationSettingsSection.tsx Refactors Convex refs to makeFunctionReference for automation settings.
apps/web/src/app/settings/AIAgentSection.tsx Refactors Convex refs and tweaks knowledge source labels.
apps/web/src/app/segments/page.tsx Refactors segments page Convex refs to makeFunctionReference.
apps/web/src/app/reports/team/page.tsx Refactors reporting queries to makeFunctionReference refs.
apps/web/src/app/reports/page.tsx Refactors reporting queries to makeFunctionReference refs.
apps/web/src/app/reports/csat/page.tsx Refactors CSAT reporting queries to makeFunctionReference refs.
apps/web/src/app/reports/conversations/page.tsx Refactors conversation reporting queries to makeFunctionReference refs.
apps/web/src/app/reports/ai/page.tsx Refactors AI reporting queries to makeFunctionReference refs and guards avgConfidence.
apps/web/src/app/outbound/outboundMessageUi.tsx Adds outbound message UI constants + icon/badge helpers.
apps/web/src/app/outbound/hooks/useOutboundMessageEditorConvex.ts Adds outbound editor Convex hook wrapper with typed refs.
apps/web/src/app/outbound/hooks/useOutboundMessageEditorController.ts Adds outbound editor controller hook to manage editor state and persistence.
apps/web/src/app/outbound/[id]/editorState.test.ts Adds unit tests for outbound editor state helpers.
apps/web/src/app/outbound/[id]/OutboundTriggerPanel.tsx Adds trigger configuration panel UI for outbound messages.
apps/web/src/app/outbound/[id]/OutboundStatisticsPanel.tsx Adds lightweight stats panel UI for outbound message performance.
apps/web/src/app/outbound/[id]/OutboundPreviewPanel.tsx Adds a preview panel for outbound messages (chat/post/banner).
apps/web/src/app/outbound/[id]/OutboundFrequencyPanel.tsx Adds frequency selection UI panel for outbound messages.
apps/web/src/app/outbound/[id]/OutboundFieldLabel.tsx Adds small label helper component for outbound editor forms.
apps/web/src/app/outbound/[id]/OutboundEditorHeader.tsx Adds outbound editor header with status controls and navigation.
apps/web/src/app/outbound/[id]/OutboundClickActionPanel.tsx Adds click action configuration panel for outbound messages.
apps/web/src/app/onboarding/page.tsx Refactors onboarding Convex refs to makeFunctionReference.
apps/web/src/app/knowledge/internal/new/page.tsx Replaces internal article creation page with a redirect to new articles flow.
apps/web/src/app/inbox/inboxRenderTypes.ts Adds typed inbox render payload shapes for the refactor.
apps/web/src/app/inbox/hooks/useInboxSuggestionsCount.ts Adds hook to fetch and track suggestions count async state.
apps/web/src/app/inbox/hooks/useInboxSelectionSync.ts Adds hook to sync selected conversation to URL query params.
apps/web/src/app/inbox/hooks/useInboxSelectionSync.test.tsx Adds tests for selection sync URL/query semantics.
apps/web/src/app/inbox/hooks/useInboxConvex.ts Adds inbox Convex wrapper hook using typed web*Ref refs.
apps/web/src/app/inbox/hooks/useInboxCompactPanels.ts Adds hook to manage compact auxiliary panel behavior.
apps/web/src/app/inbox/hooks/useInboxCompactPanels.test.tsx Adds tests for compact panel reset/toggle semantics.
apps/web/src/app/inbox/hooks/useInboxAttentionCues.ts Adds hook for unread attention cues + document title updates.
apps/web/src/app/inbox/hooks/useInboxAttentionCues.test.tsx Adds tests for title and cue suppression behavior.
apps/web/src/app/inbox/README.md Documents ownership boundaries and extension points for inbox hooks.
apps/web/src/app/inbox/InboxThreadPane.test.tsx Adds tests for consolidated knowledge/snippet picker behaviors.
apps/web/src/app/help/page.tsx Refactors help center queries to makeFunctionReference and enforces public visibility params.
apps/web/src/app/help/helpCenterLinks.ts Adds helpers for building help center URLs and reading/persisting workspace IDs.
apps/web/src/app/help/[slug]/page.tsx Refactors article page Convex refs and blocks feedback for internal articles.
apps/web/src/app/checklists/page.tsx Refactors page to use a dedicated Convex hook + shared types.
apps/web/src/app/checklists/hooks/useChecklistsPageConvex.ts Adds checklists page Convex hook wrapper.
apps/web/src/app/checklists/hooks/useChecklistBuilderConvex.ts Adds checklist builder Convex hook wrapper.
apps/web/src/app/checklists/checklistTypes.ts Extracts checklist domain types into a shared module.
apps/web/src/app/checklists/[id]/page.tsx Refactors checklist builder to use shared types and Convex hook wrapper.
apps/web/src/app/campaigns/series/[id]/seriesEditorTypes.ts Adds series editor types + utility helpers.
apps/web/src/app/campaigns/series/[id]/seriesBlockUi.tsx Adds series block icon/color helpers.
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 Refactors push campaign editor to use Convex hook wrapper and safer stats rendering.
apps/web/src/app/campaigns/page.tsx Refactors campaigns page to use Convex hook wrapper consolidated in one module.
apps/web/src/app/campaigns/hooks/useSeriesEditorConvex.ts Adds series editor Convex hook wrapper with typed refs.
apps/web/src/app/campaigns/hooks/usePushCampaignEditorConvex.ts Adds push campaign editor Convex hook wrapper with typed refs.
apps/web/src/app/campaigns/hooks/useCarouselEditorConvex.ts Adds carousel editor Convex hook wrapper with typed refs.
apps/web/src/app/campaigns/hooks/useCampaignsPageConvex.ts Adds campaigns page Convex hook wrapper with typed refs.
apps/web/src/app/campaigns/email/[id]/page.tsx Refactors email campaign editor to makeFunctionReference and safer stats rendering.
apps/web/src/app/campaigns/carousels/[id]/page.tsx Refactors carousel editor to use Convex hook wrapper and typed screen records.
apps/web/src/app/audit-logs/page.tsx Updates import path for AuditLogViewer.
apps/web/src/app/articles/hooks/useArticlesAdminConvex.ts Adds articles admin Convex hook wrapper for list/import/export/publish flows.
apps/web/src/app/articles/hooks/useArticleEditorConvex.ts Adds article editor Convex hook wrapper for assets and publish state.
apps/web/src/app/articles/hooks/useArticleCollectionsConvex.ts Adds collections management Convex hook wrapper.
apps/web/src/app/articles/collections/page.tsx Refactors collections page to use hook wrapper and hardens articleCount handling.
apps/web/src/app/articles/articlesAdminUtils.ts Adds articles admin utility helpers (filtering/signature/formatting).
apps/web/src/app/articles/articlesAdminUtils.test.ts Adds unit tests for articles admin filtering utilities.
apps/web/src/app/articles/articlesAdminTypes.ts Adds shared client-side types/constants for articles admin UI.
apps/web/src/app/articles/DeleteArticleDialog.tsx Adds delete confirmation dialog component for articles admin UI.
apps/web/package.json Updates lint command, bumps convex, and adds @opencom/web-shared.
apps/web/next.config.js Adds @opencom/web-shared to transpilePackages.
apps/web/e2e/widget.spec.ts Stabilizes widget E2E selectors/timeouts and resets volatile storage keys.
apps/web/e2e/tooltips.spec.ts Improves determinism (workspace id selection + explicit confirm button).
apps/web/e2e/snippets.spec.ts Converts auth bootstrap skip into a hard assertion for consistency.
apps/web/e2e/reports.spec.ts Simplifies auth-refresh usage (relies on global setup/fixtures).
apps/web/e2e/public-pages.spec.ts Removes nondeterministic “restricted boundary” test and documents why.
apps/web/e2e/outbound.spec.ts Replaces dialog auto-accept with explicit confirm button clicks.
apps/web/e2e/inbox.spec.ts Adds retry/error handling for convert-to-ticket and ensures suggestions sidecar opens.
apps/web/e2e/helpers/widget-helpers.ts Adds new helper waiters and improves survey/tour visibility utilities.
apps/web/e2e/helpers/storage-state.ts Adds storage-state sanitization + helpers to clear volatile widget state.
apps/web/e2e/fixtures.ts Refreshes auth and sanitizes storage state at worker boundaries.
apps/web/e2e/csat.spec.ts Makes CSAT tests deterministic per visitor via visitorKey.
apps/web/e2e/carousels.spec.ts Removes redundant auth refresh and uses explicit confirm button.
apps/web/e2e/auth.spec.ts Improves signup selectors for UI variants (“Password” / “Create Account”).
apps/web/e2e/ai-agent-settings.spec.ts Makes AI section opening deterministic via section toggle/content selectors.
apps/web/e2e/SKIP_REGISTRY.md Updates skip registry to reflect removed/changed skip conditions.
apps/mobile/src/contexts/NotificationContext.tsx Refactors push token mutations to makeFunctionReference.
apps/mobile/src/contexts/AuthContext.tsx Refactors auth/workspace/onboarding refs to makeFunctionReference and adds local shallow types.
apps/mobile/package.json Bumps convex to match web.
apps/mobile/app/_layout.tsx Simplifies route detection logic using segments.at(1) and removes index special-case.
apps/mobile/app/(app)/settings.tsx Refactors settings Convex hooks to typed makeFunctionReference refs.
apps/mobile/app/(app)/onboarding.tsx Refactors onboarding Convex refs and adds local types.
apps/mobile/app/(app)/index.tsx Refactors inbox list/presence queries to makeFunctionReference refs and handles legacy payload shapes.
apps/mobile/app/(app)/conversation/[id].tsx Refactors conversation page Convex refs to makeFunctionReference refs and adds local record types.
ROADMAP.md Expands/reorganizes roadmap items including RN/native SDK docs topics.
AGENTS.md Documents Convex TS deep-instantiation mitigation patterns and when to localize workarounds.
.github/workflows/ci.yml Adds continue-on-error gates + a final summary step that fails only when blocking checks failed.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +50 to +96
const hostedOnboardingStateQuery = makeFunctionReference<
"query",
{ workspaceId: Id<"workspaces"> },
{
status?: string;
verificationToken?: string | null;
isWidgetVerified?: boolean;
} | null
>("workspaces:getHostedOnboardingState");

const onboardingIntegrationSignalsQuery = makeFunctionReference<
"query",
{ workspaceId: Id<"workspaces"> },
{
integrations?: Array<{
id: string;
clientType: string;
clientVersion?: string;
detectedAt?: number | null;
isActiveNow?: boolean;
matchesCurrentVerificationWindow?: boolean;
origin?: string;
currentUrl?: string;
clientIdentifier?: string;
lastSeenAt?: number | null;
activeSessionCount?: number;
}>;
} | null
>("workspaces:getHostedOnboardingIntegrationSignals");

const startHostedOnboardingRef = makeFunctionReference<
"mutation",
{ workspaceId: Id<"workspaces"> },
unknown
>("workspaces:startHostedOnboarding");

const issueVerificationTokenRef = makeFunctionReference<
"mutation",
{ workspaceId: Id<"workspaces"> },
{ token: string }
>("workspaces:issueHostedOnboardingVerificationToken");

const completeWidgetStepRef = makeFunctionReference<
"mutation",
{ workspaceId: Id<"workspaces">; token?: string },
{ success: boolean }
>("workspaces:completeHostedOnboardingWidgetStep");
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

These Convex function references are created inside the component, which means they are re-instantiated every render. Move them to module scope (or memoize them) so useQuery/useMutation receive stable references and don’t risk unnecessary resubscribe/re-render churn.

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +96
const campaignQuery = makeFunctionReference<
"query",
{ id: Id<"emailCampaigns"> },
EmailCampaignRecord | null
>("emailCampaigns:get");

const campaignStatsQuery = makeFunctionReference<
"query",
{ id: Id<"emailCampaigns"> },
EmailCampaignStats
>("emailCampaigns:getStats");

const eventNamesQuery = makeFunctionReference<
"query",
{ workspaceId: Id<"workspaces"> },
string[]
>("events:getDistinctNames");

const updateCampaignRef = makeFunctionReference<"mutation", UpdateCampaignArgs, Id<"emailCampaigns">>(
"emailCampaigns:update"
);
const updateCampaign = useMutation(api.emailCampaigns.update);
const sendCampaign = useMutation(api.emailCampaigns.send);

const sendCampaignRef = makeFunctionReference<"mutation", SendCampaignArgs, SendCampaignResult>(
"emailCampaigns:send"
);
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

Same pattern as above: creating makeFunctionReference(...) values inside a React component produces new reference objects on every render. Define these refs at module scope (recommended for consistency with the other refactors in this PR) so query/mutation hooks can rely on referential stability.

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +67
<Input
type="number"
value={value.delaySeconds || 5}
onChange={(e) =>
onChange({ ...value, delaySeconds: Number.parseInt(e.target.value, 10) })
}
min={1}
/>
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

Parsing numeric inputs with Number.parseInt can yield NaN (e.g., user clears the input), which will push an invalid value into state. Consider normalizing to undefined (or a safe default) when parsing fails, so downstream trigger evaluation doesn’t receive NaN.

Copilot uses AI. Check for mistakes.
Comment on lines +74 to +82
<Input
type="number"
value={value.scrollPercent || 50}
onChange={(e) =>
onChange({ ...value, scrollPercent: Number.parseInt(e.target.value, 10) })
}
min={1}
max={100}
/>
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

Parsing numeric inputs with Number.parseInt can yield NaN (e.g., user clears the input), which will push an invalid value into state. Consider normalizing to undefined (or a safe default) when parsing fails, so downstream trigger evaluation doesn’t receive NaN.

Copilot uses AI. Check for mistakes.
>
<p className="text-sm font-medium">{content.text || "Your banner text..."}</p>
{content.dismissible !== false && (
<button className="text-white/80 hover:text-white">x</button>
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

The dismiss button uses a single-character label (“x”) without an accessible name. Add an explicit aria-label (e.g., “Dismiss banner”) or render a visually-hidden label so screen readers announce the action correctly.

Suggested change
<button className="text-white/80 hover:text-white">x</button>
<button
type="button"
className="text-white/80 hover:text-white"
aria-label="Dismiss banner"
>
x
</button>

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +28
export function getOutboundMessageStatusBadgeClass(status: OutboundMessageStatus): string {
switch (status) {
case "active":
return "bg-green-100 text-green-800";
case "draft":
return "bg-gray-100 text-gray-800";
case "paused":
return "bg-yellow-100 text-yellow-800";
case "archived":
return "bg-red-100 text-red-800";
}
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

Both switches rely on union exhaustiveness, but have no default/assert-never path, so a future union expansion can silently return undefined at runtime. Add an explicit default (or an assertNever) so behavior is predictable and TypeScript will enforce exhaustiveness.

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +47
export function OutboundMessageTypeIcon({
type,
className = "h-4 w-4",
}: OutboundMessageTypeIconProps) {
switch (type) {
case "chat":
return <MessageSquare className={className} />;
case "post":
return <Bell className={className} />;
case "banner":
return <Flag className={className} />;
}
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

Both switches rely on union exhaustiveness, but have no default/assert-never path, so a future union expansion can silently return undefined at runtime. Add an explicit default (or an assertNever) so behavior is predictable and TypeScript will enforce exhaustiveness.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +6
"use client";

import type { Id } from "@opencom/convex/dataModel";

export type ArticleEditorId = Id<"articles"> | Id<"internalArticles">;

Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

This file is a types/constants module (no React hooks/components) but is marked "use client", which can unnecessarily force client-module boundaries and increase bundling constraints in Next.js. Remove "use client" here (and in similar pure-type modules) unless there’s a concrete need for client-only semantics.

Suggested change
"use client";
import type { Id } from "@opencom/convex/dataModel";
export type ArticleEditorId = Id<"articles"> | Id<"internalArticles">;
import type { Id } from "@opencom/convex/dataModel";
export type ArticleEditorId = Id<"articles"> | Id<"internalArticles">;

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +25
export function buildHelpCenterHref(options?: {
workspaceId?: WorkspaceIdLike;
collectionSlug?: string | null;
}): string {
const params = new URLSearchParams();
if (options?.collectionSlug) {
params.set("collection", options.collectionSlug);
}
if (options?.workspaceId) {
params.set("workspaceId", options.workspaceId);
}
return buildHelpHref("/help", params);
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

This new module introduces routing/localStorage logic that can be unit-tested deterministically (URL building + storage fallback behavior). Consider adding a small vitest suite for these helpers to lock in query param behavior and storage parsing fallbacks.

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +64
export function readStoredPublicWorkspaceId(): string | null {
if (typeof window === "undefined") {
return null;
}

const rawValue = window.localStorage.getItem(PUBLIC_WORKSPACE_STORAGE_KEY);
if (rawValue && rawValue.length > 0) {
return rawValue;
}

return readStoredActiveWorkspaceId();
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

This new module introduces routing/localStorage logic that can be unit-tested deterministically (URL building + storage fallback behavior). Consider adding a small vitest suite for these helpers to lock in query param behavior and storage parsing fallbacks.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Why do we use local storage for this? @copilot
For help center links, why do we need to read active workspaceID from local storage?
Dont the public links have the specific workspace ID in the URLs, and if we are linking to helpcenter in the widget, why do we need to pull active workspace id?

@djanogly djanogly review requested due to automatic review settings March 23, 2026 18:28
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.

2 participants