Skip to content

feat(platform): auto-provision conversation sync and fix empty states#490

Merged
Israeltheminer merged 2 commits into
mainfrom
feat/conversation-sync-and-empty-state
Feb 17, 2026
Merged

feat(platform): auto-provision conversation sync and fix empty states#490
Israeltheminer merged 2 commits into
mainfrom
feat/conversation-sync-and-empty-state

Conversation

@Israeltheminer

@Israeltheminer Israeltheminer commented Feb 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Auto-provision conversation sync: When an integration with messaging capabilities (e.g. list_messages) is connected, a conversation sync workflow is automatically created and scheduled — matching the website scan auto-provisioning pattern. Works generically with any messaging integration.
  • Fix conversations empty state: The "Activate conversations" CTA now only shows when the org has zero conversations. When a status tab is empty but conversations exist elsewhere, the conversation list shows an inline empty view instead.
  • Fix workflow schedule registration: Provisioning functions (save_default_workflows, provision_website_scan_workflow, provision_conversation_sync_workflow) now create wfSchedules entries via a new provisionSchedule internal mutation, so the cron scanner picks up scheduled workflows. Previously, schedules were only created by the trigger_steps_to_start migration, causing any newly provisioned workflow to run once and never again.
  • Backfill migration: Added backfill_workflow_schedules migration to create missing wfSchedules entries for existing active workflows with scheduled start steps.

Test plan

  • Connect a new messaging integration and verify a "Conversation Sync" workflow is auto-created and scheduled
  • Verify the sync runs on schedule (check wfSchedules table has an entry)
  • Add a new website and verify the website scan workflow also gets a wfSchedules entry
  • Verify conversations empty state: org with no conversations shows "Activate conversations" CTA
  • Verify conversations empty state: org with conversations but empty tab shows inline "No conversations in this tab"
  • Run backfill_workflow_schedules migration and verify it creates missing schedule entries
  • Run existing tests: provision_conversation_sync_workflow.test.ts

Summary by CodeRabbit

Release Notes

  • New Features

    • Automatic conversation synchronization workflows now provision for messaging integrations upon creation, enabling seamless message syncing
    • Enhanced empty state messaging throughout conversations interface with localized, user-friendly text
  • UI Updates

    • Refined email preview component styling, including updated button appearance and container design

Auto-provision a conversation sync workflow when an integration with
messaging capabilities is connected, matching the pattern used for
website scan workflows. The sync is generic and works with any
integration that has message listing operations.

Also fix the conversations empty state so the "Activate conversations"
CTA only shows when the org has zero conversations. When a status tab
is empty but conversations exist elsewhere, the list shows an inline
empty view instead.

Additionally fix workflow schedule registration: provisioning functions
now create wfSchedules entries so the cron scanner picks up scheduled
workflows. Previously schedules were only created by a one-time
migration, causing newly provisioned workflows to never run on schedule.
@coderabbitai

coderabbitai Bot commented Feb 17, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR introduces a conversation synchronization system for integrations. It adds a hasConversations boolean flag to the conversations client component for improved empty-state management. The backend introduces a new "Conversation Sync" workflow that automatically provisions for integrations supporting message-list operations, handles both threaded and non-threaded message ingestion, and registers cron-based scheduling. Supporting utilities detect message and thread capabilities, infer communication channels from integration names, and manage workflow lifecycle. A minor UI styling update to the email preview component is also included.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main objectives: auto-provisioning conversation sync workflows and fixing empty states in the conversations UI.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/conversation-sync-and-empty-state

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@services/platform/app/features/conversations/components/conversations-client.tsx`:
- Around line 101-103: The component currently renders
ActivateConversationsEmptyState when hasConversations is false even while the
query is loading; update the useHasConversations call to return its loading
state (isLoading) and change the render guard in conversations-client.tsx to
only show ActivateConversationsEmptyState when loading is false and
hasConversations is false (e.g., if (!isLoading && !hasConversations) ...),
ensuring you propagate isLoading from useHasConversations through any
intermediate hooks/props if needed.

In `@services/platform/convex/workflows/triggers/internal_mutations.ts`:
- Around line 68-88: provisionSchedule currently always inserts into wfSchedules
which can create duplicate cron entries on retries; modify provisionSchedule to
first query wfSchedules for an existing active schedule matching organizationId,
workflowRootId, cronExpression and timezone (and createdBy if needed) and if
found return its Id instead of inserting; otherwise insert as before; use the
wfSchedules index that covers (organizationId, workflowRootId, cronExpression,
timezone, isActive) to make the check efficient and return the existing row's id
when present.

Comment thread services/platform/app/features/conversations/components/conversations-client.tsx Outdated
Comment thread services/platform/convex/workflows/triggers/internal_mutations.ts
…heck

Prevent briefly flashing the "Activate conversations" empty state while
the hasConversations query is still loading by checking isLoading before
rendering the empty state.
@greptile-apps

greptile-apps Bot commented Feb 17, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces auto-provisioning of a "Conversation Sync" workflow when a messaging integration is connected, fixes the conversations empty state so the "Activate conversations" CTA only appears when an org has zero conversations, and fixes a pre-existing bug where provisioned scheduled workflows never got a wfSchedules entry (causing them to run only once and never again on schedule).

Key changes and findings:

  • provisionSchedule has no duplicate guard — the new internalMutation inserts directly into wfSchedules without checking for an existing entry for the same workflowRootId. If initializeDefaultWorkflows is re-run for an existing org, or if any provisioning function is retried, duplicate schedule rows accumulate and the cron scanner fires each workflow multiple times per tick.
  • hasConversations loading state causes a CTA flashuseHasConversations returns isLoading but the route discards it. On re-navigation, useCachedPaginatedQuery returns its cached result immediately (bypassing the skeleton guard), while hasConversations still defaults to false until its query resolves. This briefly renders the "Activate conversations" CTA for orgs that already have conversations.
  • provisionConversationSyncWorkflow has no idempotency check — unlike backfillConversationSync (which checks metadata.conversationSyncWorkflowId), the core provision function creates a new workflow and schedule unconditionally on every call, creating duplicates if the Convex action is retried.
  • The backfill_workflow_schedules migration correctly uses the by_workflowRoot index to skip already-present schedules and follows the same pattern as the existing trigger_steps_to_start migration.
  • The show-quoted-email button in email-preview.tsx loses its hover:text-foreground class while retaining transition-colors, leaving the transition with nothing to animate.

Confidence Score: 3/5

  • Mergeable with caution — two logic issues (duplicate schedules, CTA flash) need addressing before this ships to production orgs with existing data.
  • The core provisioning logic is sound and follows established patterns. However, the missing idempotency guard in provisionSchedule is a data-integrity risk that compounds on every re-run, and the hasConversations loading race will visibly surface the wrong empty state for existing users on re-navigation. Both are reproducible in normal usage, not just edge cases.
  • services/platform/convex/workflows/triggers/internal_mutations.ts (missing duplicate guard in provisionSchedule) and services/platform/app/routes/dashboard/$id/conversations/$status.tsx (discarded isLoading from useHasConversations).

Important Files Changed

Filename Overview
services/platform/convex/integrations/provision_conversation_sync_workflow.ts Core provisioning function — creates workflow, publishes it, registers schedule, updates integration metadata. Does not check for an existing sync workflow before provisioning, creating a duplicate risk if triggered more than once per integration.
services/platform/convex/workflows/triggers/internal_mutations.ts New provisionSchedule mutation inserts directly into wfSchedules without checking for an existing entry for the same workflowRootId, making duplicate schedule records possible if called more than once for the same workflow.
services/platform/app/routes/dashboard/$id/conversations/$status.tsx Uses useHasConversations but discards isLoading — on re-navigation with a cached paginated result but uncached hasConversations, hasConversations defaults to false and the "Activate conversations" CTA will flash before the query resolves.
services/platform/convex/integrations/create_integration.ts Correctly schedules conversation sync provisioning via ctx.scheduler.runAfter for integrations with message listing operations. Error is caught and logged rather than surfaced, which is appropriate for a non-critical background operation.
services/platform/convex/organizations/save_default_workflows.ts Now calls provisionSchedule for each default workflow after creation, but there is no guard against initializeDefaultWorkflows being called more than once for the same org — it would insert duplicate schedule records.
services/platform/convex/migrations/backfill_workflow_schedules.ts Correctly backfills wfSchedules entries for active scheduled workflows using a by_workflowRoot index to skip existing schedules. Iterates all wfStepDefs via for await (more memory-efficient than .collect()); N+1 lookups per step could time out on very large datasets, but this pattern mirrors existing migrations.
services/platform/app/components/ui/data-display/email-preview.tsx Show-quoted-email button styling updated: border-radius removed from the pre block CSS, button gets padding and rounded corners. hover:text-foreground and mt-2 are removed — the button now has no hover color change.

Sequence Diagram

sequenceDiagram
    actor User
    participant CI as createIntegration
    participant Sched as ctx.scheduler
    participant PCS as provisionConversationSync (action)
    participant PCSW as provisionConversationSyncWorkflow
    participant WFDef as wf_definitions.internal_mutations
    participant Triggers as workflows.triggers.internal_mutations
    participant IntMeta as integrations.internal_mutations

    User->>CI: create integration (with list_messages op)
    CI->>CI: hasMessageListOperation() → true
    CI->>Sched: runAfter(0, provisionConversationSync, args)
    CI-->>User: integrationId

    Note over Sched,PCSW: Runs asynchronously after integration creation
    Sched->>PCS: provisionConversationSync(args)
    PCS->>PCSW: provisionConversationSyncWorkflow(ctx, args)
    PCSW->>WFDef: provisionWorkflowWithSteps(...)
    WFDef-->>PCSW: { workflowId }
    PCSW->>WFDef: provisionPublishDraft(workflowId)
    PCSW->>Triggers: provisionSchedule(workflowRootId, cronExpression)
    Note over Triggers: ⚠️ No duplicate check
    Triggers-->>PCSW: wfScheduleId
    PCSW->>IntMeta: updateIntegration(metadata.conversationSyncWorkflowId)
    PCSW->>Sched: runAfter(0, startWorkflow) — initial sync
Loading

Last reviewed commit: 2629499

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

18 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

Comment thread services/platform/convex/workflows/triggers/internal_mutations.ts
Comment thread services/platform/app/components/ui/data-display/email-preview.tsx
@Israeltheminer Israeltheminer merged commit 3eba1a9 into main Feb 17, 2026
17 checks passed
@Israeltheminer Israeltheminer deleted the feat/conversation-sync-and-empty-state branch February 17, 2026 19:17
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