Skip to content

Conversation

@hbjORbj
Copy link
Contributor

@hbjORbj hbjORbj commented Nov 22, 2025

What does this PR do?

This PR completes the circular dependency fix between the workflows and billing packages by making creditCheckFn a required parameter throughout the workflow reminder system, eliminating the dynamic import fallback entirely.

Context: This builds on PR #25312 which fixed the reminderScheduler circular dependency. This PR addresses the remaining circular dependency in messageDispatcher and related functions.

The circular dependency:

credit-service → reminderScheduler → smsReminderManager → messageDispatcher → credit-service (dynamic import)

The solution: Use dependency injection by making creditCheckFn required and wiring it from all callers (which live outside the workflows package and can safely import CreditService).

Mandatory Tasks

  • I have self-reviewed the code
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. N/A - internal refactoring only
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

…jection

Break the 4-file circular dependency chain:
credit-service → reminderScheduler → smsReminderManager → messageDispatcher → credit-service

Solution:
- Add optional creditCheckFn parameter to messageDispatcher functions
- Thread creditCheckFn through the call chain: scheduleWorkflowReminders → scheduleSMSReminder/scheduleWhatsappReminder → messageDispatcher
- When creditCheckFn is provided, use it; otherwise fall back to dynamic CreditService import for backward compatibility
- This breaks the workflows → billing import while preserving immediate fallback behavior

Changes:
- messageDispatcher: Accept optional creditCheckFn parameter, use it if provided
- smsReminderManager: Thread creditCheckFn through scheduleSMSReminder
- whatsappReminderManager: Thread creditCheckFn through scheduleWhatsappReminder
- reminderScheduler: Add creditCheckFn to ScheduleWorkflowRemindersArgs and pass through processWorkflowStep

All type checks, lint checks, and unit tests pass.

Co-Authored-By: [email protected] <[email protected]>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

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

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

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

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

…ency fix

- Add creditCheckFn parameter to WorkflowService.scheduleFormWorkflows
- Wire creditCheckFn from all 10 entry points that call workflow scheduling:
  * formSubmissionUtils.ts (form submissions)
  * roundRobinManualReassignment.ts (round-robin reassignment)
  * triggerFormSubmittedNoEventWorkflow.ts (form workflow trigger)
  * handleBookingRequested.ts (booking requests)
  * RegularBookingService.ts (2 calls - payment initiated & new bookings)
  * handleSeats.ts (seated bookings)
  * handleConfirmation.ts (2 calls - confirmation & payment)
  * handleMarkNoShow.ts (no-show updates)
  * confirm.handler.ts (booking rejection)
- Update test expectations to use expect.objectContaining()
- Fix pre-existing lint warning in handleMarkNoShow.ts (any type)
- This completes the messageDispatcher circular dependency fix by ensuring
  creditCheckFn is actually passed through the call chain, breaking the
  4-file circular dependency at runtime

Co-Authored-By: [email protected] <[email protected]>
@vercel
Copy link

vercel bot commented Nov 24, 2025

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

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal Ignored Ignored Nov 26, 2025 0:34am
cal-eu Ignored Ignored Nov 26, 2025 0:34am

… check error

- Replace constrained type with generic type parameter
- Add proper type guard for rejected promises
- Fixes CI type check failure in handleMarkNoShow.ts:385
- Avoids 'any' type while accepting any fulfilled value shape

Co-Authored-By: [email protected] <[email protected]>
@github-actions
Copy link
Contributor

github-actions bot commented Nov 24, 2025

E2E results are ready!

devin-ai-integration bot and others added 2 commits November 25, 2025 04:05
…backs

- Wire creditCheckFn in packages/sms/sms-manager.ts (can safely import CreditService)
- Create makeHandler factory pattern for CRON endpoints (scheduleSMSReminders.ts, scheduleWhatsappReminders.ts)
- Wire creditCheckFn from apps/web CRON routes to factories
- Add warning log in messageDispatcher when fallback is used
- Complete creditCheckFn wiring from all direct callers (activateEventType.handler.ts, util.ts)

This eliminates all fallbacks to dynamic import except as a safety net for unforeseen call sites.
The circular dependency (workflows ↔ billing) remains acceptable as discussed with user (Option C).

Co-Authored-By: [email protected] <[email protected]>
The scheduleFormWorkflows function now receives creditCheckFn as a parameter.
Updated test assertions to use expect.objectContaining() with creditCheckFn: expect.any(Function)
to account for the new dependency injection parameter.

Co-Authored-By: [email protected] <[email protected]>
The sendSmsOrFallbackEmail function now receives creditCheckFn as a parameter.
Updated test assertion to use expect.objectContaining() with creditCheckFn: expect.any(Function)
to account for the new dependency injection parameter. Also removed teamId: undefined
assertion as the key may be omitted entirely from the actual call.

Co-Authored-By: [email protected] <[email protected]>
devin-ai-integration bot and others added 2 commits November 25, 2025 06:10
This commit completes the circular dependency fix by making creditCheckFn
required throughout the call chain, eliminating the dynamic import fallback
entirely.

Changes:
- Make creditCheckFn required in messageDispatcher functions (sendSmsOrFallbackEmail, scheduleSmsOrFallbackEmail)
- Remove dynamic import fallback and warning log from messageDispatcher
- Make creditCheckFn required in ScheduleTextReminderArgs (smsReminderManager)
- Make creditCheckFn required in processWorkflowStep and ScheduleWorkflowRemindersArgs (reminderScheduler)
- Add creditCheckFn to SendCancelledRemindersArgs and wire from handleCancelBooking

The circular dependency is now fully broken - no more dynamic imports of
CreditService from within the workflows package. All callers must explicitly
provide creditCheckFn via dependency injection.

Co-Authored-By: [email protected] <[email protected]>
…lows

This commit fixes the CI type check error by making creditCheckFn required
in WorkflowService.scheduleFormWorkflows. Previously, creditCheckFn was
optional in scheduleFormWorkflows but required in scheduleWorkflowReminders,
causing a type mismatch.

Changes:
- Make creditCheckFn required in scheduleFormWorkflows signature
- Update WorkflowService.test.ts to pass mock creditCheckFn in all test cases
- Add responseId and routedEventTypeId to test calls for completeness

All callers of scheduleFormWorkflows already pass creditCheckFn, so this
change is safe and completes the circular dependency fix.

Co-Authored-By: [email protected] <[email protected]>
@hbjORbj hbjORbj changed the title fix: break circular dependency by making creditCheckFn required fix: break circular dependency by passing creditCheckFn Nov 25, 2025
@hbjORbj hbjORbj changed the title fix: break circular dependency by passing creditCheckFn fix: break circular dependency by passing creditCheckFn in messageDispatcher Nov 25, 2025
@hbjORbj hbjORbj marked this pull request as ready for review November 25, 2025 08:44
@hbjORbj hbjORbj requested a review from a team as a code owner November 25, 2025 08:44
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

13 issues found across 24 files

Prompt for AI agents (all 13 issues)

Understand the root cause of the following 13 issues and fix them.


<file name="packages/features/handleMarkNoShow.ts">

<violation number="1" location="packages/features/handleMarkNoShow.ts:307">
Bind CreditService.hasAvailableCredits before passing it as creditCheckFn; the current unbound method loses its this context and will throw at runtime, preventing reminders from being sent.</violation>
</file>

<file name="packages/features/ee/workflows/api/scheduleWhatsappReminders.ts">

<violation number="1" location="packages/features/ee/workflows/api/scheduleWhatsappReminders.ts:130">
Bind CreditService.hasAvailableCredits before passing it to scheduleSmsOrFallbackEmail; otherwise the callback runs with the wrong `this` and crashes when it calls private helpers.</violation>
</file>

<file name="packages/sms/sms-manager.ts">

<violation number="1" location="packages/sms/sms-manager.ts:51">
Passing `creditService.hasAvailableCredits` unbound drops its `this` context, so the credit check throws at runtime. Bind or wrap the method before injecting it.</violation>
</file>

<file name="packages/features/bookings/lib/handleSeats/handleSeats.ts">

<violation number="1" location="packages/features/bookings/lib/handleSeats/handleSeats.ts:135">
Bind the CreditService instance when wiring creditCheckFn; passing the method unbound causes runtime failures when messageDispatcher calls it without a `this` context.</violation>
</file>

<file name="packages/app-store/routing-forms/lib/formSubmissionUtils.ts">

<violation number="1" location="packages/app-store/routing-forms/lib/formSubmissionUtils.ts:236">
Passing the instance method without binding drops its `this` context, so any workflow reminder credit check will throw. Bind the method before passing it.</violation>
</file>

<file name="packages/features/bookings/lib/handleBookingRequested.ts">

<violation number="1" location="packages/features/bookings/lib/handleBookingRequested.ts:123">
Bind the CreditService instance when passing `creditCheckFn`; otherwise `this` becomes undefined and credit checks throw once the limit-reached path executes.</violation>
</file>

<file name="packages/features/ee/workflows/api/scheduleSMSReminders.ts">

<violation number="1" location="packages/features/ee/workflows/api/scheduleSMSReminders.ts:199">
creditCheckFn is passed an unbound instance method, so inside messageDispatcher `this` becomes undefined and credit checks throw before reminders can be scheduled. Bind the method to the CreditService instance.</violation>
</file>

<file name="packages/trpc/server/routers/viewer/workflows/activateEventType.handler.ts">

<violation number="1" location="packages/trpc/server/routers/viewer/workflows/activateEventType.handler.ts:425">
`creditService.hasAvailableCredits` is passed unbound, so `this` is undefined when the reminder invokes `creditCheckFn`, crashing credit checks.</violation>
</file>

<file name="packages/features/ee/round-robin/roundRobinManualReassignment.ts">

<violation number="1" location="packages/features/ee/round-robin/roundRobinManualReassignment.ts:618">
`creditCheckFn` is passed an unbound instance method, so `hasAvailableCredits` will throw when accessed without its `this` context.</violation>
</file>

<file name="packages/features/bookings/lib/handleCancelBooking.ts">

<violation number="1" location="packages/features/bookings/lib/handleCancelBooking.ts:377">
Bind CreditService.hasAvailableCredits before passing it as creditCheckFn; otherwise the callback throws because it relies on this.</violation>
</file>

<file name="packages/features/bookings/lib/handleConfirmation.ts">

<violation number="1" location="packages/features/bookings/lib/handleConfirmation.ts:374">
Passing `creditService.hasAvailableCredits` directly loses the `this` binding, so workflow reminders will crash when `creditCheckFn` runs. Bind the method (e.g., `.bind(creditService)` or wrap in a lambda) before passing it down.</violation>
</file>

<file name="packages/features/bookings/lib/service/RegularBookingService.ts">

<violation number="1" location="packages/features/bookings/lib/service/RegularBookingService.ts:2391">
Bind the hasAvailableCredits method before passing it into scheduleWorkflowsForNewBooking so the callback keeps the CreditService instance context.</violation>
</file>

<file name="packages/trpc/server/routers/viewer/workflows/util.ts">

<violation number="1" location="packages/trpc/server/routers/viewer/workflows/util.ts:847">
`creditService.hasAvailableCredits` is passed without binding, so the credit check throws when messageDispatcher calls it. Bind the method to the instance before injecting.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

t: TFunction;
replyTo: string;
};
creditCheckFn: CreditCheckFn;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

key change

replyTo: string;
workflowStepId?: number;
};
creditCheckFn: CreditCheckFn;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

key change

@hbjORbj hbjORbj marked this pull request as ready for review November 26, 2025 02:47
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 24 files

Comment on lines 48 to 49
const isRejected = <T>(x: PromiseSettledResult<T>): x is PromiseRejectedResult => x.status === "rejected";

Copy link
Contributor

Choose a reason for hiding this comment

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

is this used somewhere ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah no, will revert now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

reverted

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 24 files

@hbjORbj hbjORbj merged commit d9cddd8 into main Nov 27, 2025
87 of 92 checks passed
@hbjORbj hbjORbj deleted the devin/messagedispatcher-circular-1763790475 branch November 27, 2025 07:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants