Skip to content

Conversation

@warrenbhw
Copy link

@warrenbhw warrenbhw commented Apr 1, 2025

What does this PR do?

  • Fixes Multiple Round Robin Hosts #11953
  • Adds support for selecting multiple hosts in round-robin event types.
  • Modifies getLuckyUser to return either a single host (if multipleRoundRobinHost is not specified or is ) or multiple hosts (if multipleRoundRobinHosts > 1).
  • Updates the event type metadata schema EventType.metadata) to include multipleRoundRobinHosts`.
  • Adds comprehensive test coverage for multiple host selection scenarios.
  • Enhances the booking flow so that multiple hosts can be assigned to a single booking if configured.

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be ejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, check this box and write “N/A” here.) N/A
  • I confirm automated tests are in place that prove my fix is effective or that y feature works.

How should this be tested?

  1. Setup: Pull this branch and run any necessary migrations.
  2. Create or edit an Event Type:
  • Under the “Round Robin” settings, ensure there are no fixed hosts.
  • Add multiple users as round-robin hosts.
  • In the EventType.metadata, set multipleRoundRobinHosts to the desired number e.g., 2 or 3).
  1. Book an Event:
  • Book the event as a normal user and confirm that multiple hosts are indeed elected.
  • Verify in the database or in the logs that the expected hosts are assigned to he booking.
  1. Check Calendar/Email/Webhook (if enabled):
  • Each of the assigned hosts should receive notification (and appear as attendees f your configuration supports that).
  • Any configured webhooks should fire as usual, including references to all elected hosts.

No additional environment variables are required for this feature.

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have verified that the changes generate no new warnings.
  • New and existing unit tests pass locally with my changes.

**

- Add support for selecting multiple hosts in round-robin event types
- Modify getLuckyUser to handle multiple host selection
- Update event type metadata schema to include multipleRoundRobinHosts
- Add comprehensive test coverage for multiple host selection scenarios
- Enhance booking flow to accommodate multiple hosts
…arameters

- Streamline type generics for more flexible host selection
- Remove unnecessary parameters from function calls
- Improve type safety for returned users
@warrenbhw warrenbhw requested review from OliverGilan, califlower and moffmiffleton and removed request for OliverGilan and califlower April 2, 2025 00:19
...getLuckyUserParams,
eventType,
availableUsers: remainingAvailableUsers as [AvailableUser, ...AvailableUser[]],
availableUsers: remainingAvailableUsers as [T, ...T[]],

Choose a reason for hiding this comment

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

test


// Normalize reassignedRRHost to always be a single user object
const reassignedRRHost = Array.isArray(reassignedRRHostResult)
? reassignedRRHostResult[0]

Choose a reason for hiding this comment

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

test

const currentBookingTitle = booking.title;
let newBookingTitle = currentBookingTitle;

const reassignedRRHostT = await getTranslation(reassignedRRHost.locale || "en", "common");

Choose a reason for hiding this comment

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

test

await ensureAvailableUsers(
{ ...eventTypeWithUsers, users: [user] },
{
dateFrom: dayjs(start).tz(reqBody.timeZone).format(),

Choose a reason for hiding this comment

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

test


// Handle both cases - single user and multiple users
const usersToProcess = Array.isArray(newLuckyUsers) ? newLuckyUsers : [newLuckyUser];

Choose a reason for hiding this comment

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

yoot

},
loggerWithEventDetails,
shouldServeCache
// for recurring round robin events check if lucky users are available for next slots

Choose a reason for hiding this comment

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

log

break; // prevent infinite loop
}

// Handle both cases - single user and multiple users
Copy link
Author

Choose a reason for hiding this comment

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

one two three

}

// Handle both cases - single user and multiple users
const usersToProcess = Array.isArray(newLuckyUsers) ? newLuckyUsers : [newLuckyUser];
Copy link
Author

Choose a reason for hiding this comment

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

asdf

// for recurring round robin events check if lucky users are available for next slots
for (const user of usersToProcess) {
try {
for (
Copy link
Author

Choose a reason for hiding this comment

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

asdf

{ ...eventTypeWithUsers, users: [newLuckyUser] },
{
dateFrom: dayjs(start).tz(reqBody.timeZone).format(),
dateTo: dayjs(end).tz(reqBody.timeZone).format(),
Copy link
Author

Choose a reason for hiding this comment

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

foo

}
// if no error, then lucky user is available for the next slots
luckyUsers.push(newLuckyUser);
} catch {
Copy link
Author

Choose a reason for hiding this comment

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

foobar

i < req.body.allRecurringDates.length && i < req.body.numSlotsToCheckForAvailability;
i++
) {
const start = req.body.allRecurringDates[i].start;
Copy link
Author

Choose a reason for hiding this comment

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

foobar

Copy link
Author

boop


if (numberOfHostsToSelect === 1) {
// Original single-host selection logic
const { luckyUser } = getLuckyUser_requiresDataToBePreFetched({
Copy link
Author

Choose a reason for hiding this comment

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

;lkajsd;flka

Comment on lines +674 to 679
const numberOfHostsToSelect = eventType.metadata?.multipleRoundRobinHosts || 1;
// loop through all non-fixed hosts and get the lucky users
// This logic doesn't run when contactOwner is used because in that case, luckUsers.length === 1
while (luckyUserPool.length > 0 && luckyUsers.length < 1 /* TODO: Add variable */) {
while (luckyUserPool.length > 0 && luckyUsers.length < numberOfHostsToSelect) {
const freeUsers = luckyUserPool.filter(
(user) => !luckyUsers.concat(notAvailableLuckyUsers).find((existing) => existing.id === user.id)
Copy link
Author

Choose a reason for hiding this comment

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

asdf

Comment on lines +691 to +694
// Check if we need to select multiple hosts
const numberOfHostsToSelect = eventType.metadata?.multipleRoundRobinHosts || 1;

const newLuckyUsers = await getLuckyUser({
Copy link
Author

Choose a reason for hiding this comment

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

bruh

Comment on lines 703 to 711
eventType,
routingFormResponse,
numberOfHostsToSelect,
});

// Handle single vs multiple users being returned
const newLuckyUser = Array.isArray(newLuckyUsers) ? newLuckyUsers[0] : newLuckyUsers;
if (!newLuckyUser) {
break; // prevent infinite loop
Copy link
Author

Choose a reason for hiding this comment

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

bruh2

Comment on lines +65 to +75
{ id: 103, email: "[email protected]" },
];

const mockParams = {
availableUsers: mockAvailableUsers as any,
eventType: {
id: mockEventTypeId,
isRRWeightsEnabled: false,
team: {},
},
allRRHosts: [
Copy link
Author

Choose a reason for hiding this comment

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

a;sldkf

Comment on lines +93 to +114
// Check that the result is an array with 2 items
const result = await getLuckyUser(mockParams);

// Verify result is an array
expect(Array.isArray(result)).toBe(true);

// Verify correct number of hosts returned
expect(result.length).toBe(2);
},
timeout
);

// Complete test of the full booking flow with multiple hosts
test(
"successfully creates a booking with multiple round-robin hosts",
async ({ emails }) => {
const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default;

const subscriberUrl = "http://my-webhook.example.com";
const booker = getBooker({
email: "[email protected]",
name: "Booker",
Copy link
Author

Choose a reason for hiding this comment

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

;alksjdf;lkaj sdf

}
>(getLuckyUserParams: GetLuckyUserParams<T>) {
Copy link
Author

Choose a reason for hiding this comment

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

flop

Comment on lines -441 to -446
currentMonthBookingsOfAvailableUsers,
bookingsOfNotAvailableUsersOfThisMonth,
allRRHostsBookingsOfThisMonth,
allRRHostsCreatedThisMonth,
organizersWithLastCreated,
attributeWeights,
Copy link
Author

Choose a reason for hiding this comment

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

test one

Comment on lines +30 to +41
// Mock the getLuckyUser function to return an array of users instead of a single user
vi.mock("@calcom/lib/server/getLuckyUser", () => ({
getLuckyUser: vi.fn().mockImplementation(({ numberOfHostsToSelect }) => {
// Create the appropriate number of hosts based on the request
const hosts = [];
for (let i = 1; i <= numberOfHostsToSelect; i++) {
hosts.push({
id: 100 + i, // First host ID 101, second 102, etc.
name: `Host ${i}`,
email: `host${i}@example.com`,
timeZone: "Europe/London",
credentials: [], // Will be populated later
Copy link
Author

Choose a reason for hiding this comment

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

fooar

@warrenbhw warrenbhw requested review from moffmiffleton and removed request for moffmiffleton May 12, 2025 01:38
Copy link
Author

asdf

Copy link
Author

asdf234

Comment on lines +37 to +48
id: 100 + i, // First host ID 101, second 102, etc.
name: `Host ${i}`,
email: `host${i}@example.com`,
timeZone: "Europe/London",
credentials: [], // Will be populated later
selectedCalendars: [],
});
}
return Promise.resolve(hosts);
}),
}));

Copy link
Author

Choose a reason for hiding this comment

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

test comment

Comment on lines +77 to +82
user: { id: 101, email: "[email protected]", credentials: [], userLevelSelectedCalendars: [] },
createdAt: new Date(),
},
{
user: { id: 102, email: "[email protected]", credentials: [], userLevelSelectedCalendars: [] },
createdAt: new Date(),
Copy link
Author

Choose a reason for hiding this comment

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

;lkj asd;lkfj

@warrenbhw warrenbhw changed the title feat: Support multiple round-robin host selection feat: Support multiple round-robin host selection casey May 14, 2025
@warrenbhw warrenbhw changed the title feat: Support multiple round-robin host selection casey feat: Support multiple round-robin host selection casey traina May 14, 2025
@warrenbhw warrenbhw changed the title feat: Support multiple round-robin host selection casey traina feat: Support multiple round-robin host selection May 14, 2025
Comment on lines +1291 to +1304
},
allRRHosts,
routingFormResponse: null,
numberOfHostsToSelect: 2,
});

// Verify we get an array with the first 2 users
expect(Array.isArray(result)).toBe(true);
expect(result).toHaveLength(2);
// The first user should be the least recently booked
expect(result[0].id).toBe(users[2].id);
});

it("returns a single user when numberOfHostsToSelect is not provided", async () => {
Copy link
Author

Choose a reason for hiding this comment

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

asdf;lkj ;alksdjf

Copy link

@mesa-dot-dev mesa-dot-dev bot left a comment

Choose a reason for hiding this comment

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

What Changed

This PR introduces a significant enhancement to round-robin scheduling by allowing multiple hosts to be selected for a single event. The core changes include updating the EventType.metadata schema with a new multipleRoundRobinHosts field and refactoring the getLuckyUser function to return an array of users based on this setting. The booking creation flow in handleNewBooking.ts has been updated to accommodate this array of hosts, ensuring they are all correctly assigned to the new booking. Comprehensive new tests have been added to validate this multi-host scenario.

Risks / Concerns

The review surfaced a few medium-severity risks. The reassignment logic in roundRobinReassignment.ts does not account for cases where getLuckyUser might return an empty array, which could lead to downstream errors. Additionally, the testing has some gaps: a test for adding hosts as attendees doesn't fully simulate the behavior, and another test for multi-host selection only validates the first user in the returned array, not the complete order.

8 files reviewed | 3 comments | Review on Mesa | Edit Reviewer Settings

},
});

// The test doesn't accurately simulate adding the hosts as attendees,
Copy link

Choose a reason for hiding this comment

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

This comment indicates the test isn't fully validating the core functionality. The test claims to verify that "booking includes all selected hosts as attendees" but then admits it doesn't accurately simulate adding hosts as attendees. Consider either:

  1. Implementing proper host-as-attendee simulation in the test
  2. Renaming the test to reflect what it actually validates
  3. Adding a TODO comment with plans to implement full validation

This could lead to false confidence that the feature is working correctly when it might not be.

Type: Logic | Severity: Medium

});

// Normalize reassignedRRHost to always be a single user object
const reassignedRRHost = Array.isArray(reassignedRRHostResult)
Copy link

Choose a reason for hiding this comment

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

Consider adding a check for empty arrays. If getLuckyUser returns an empty array, reassignedRRHostResult[0] will be undefined, which could cause issues downstream. You might want to add: const reassignedRRHost = Array.isArray(reassignedRRHostResult) ? reassignedRRHostResult[0] : reassignedRRHostResult; if (!reassignedRRHost) { throw new Error('No available host found for reassignment'); }

Type: Logic | Severity: Medium

expect(Array.isArray(result)).toBe(true);
expect(result).toHaveLength(2);
// The first user should be the least recently booked
expect(result[0].id).toBe(users[2].id);
Copy link

Choose a reason for hiding this comment

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

This assertion only checks the first user's ID, but for a more comprehensive test, consider also checking the second user's ID to ensure the ordering is completely correct. For example: expect(result[1].id).toBe(users[1].id);

Type: Logic | Severity: Medium

return cred.delegatedToId === id.delegationCredentialId;
} else if (id.credentialId) {
}
if (id.credentialId) {

Choose a reason for hiding this comment

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

asdf

Choose a reason for hiding this comment

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

asdf;lkja sdf

Choose a reason for hiding this comment

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

blah

Comment on lines +81 to +89
user: { id: 102, email: "[email protected]", credentials: [], userLevelSelectedCalendars: [] },
createdAt: new Date(),
},
{
user: { id: 103, email: "[email protected]", credentials: [], userLevelSelectedCalendars: [] },
createdAt: new Date(),
},
],
routingFormResponse: null,
Copy link
Author

Choose a reason for hiding this comment

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

asdf

Comment on lines +93 to +94
// Check that the result is an array with 2 items
const result = await getLuckyUser(mockParams);
Copy link
Author

Choose a reason for hiding this comment

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

zxcvzxcv

Comment on lines 426 to 427
weight?: number | null;
}

Choose a reason for hiding this comment

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

test

@OliverGilan
Copy link

test

Comment on lines +1207 to +1213
username: "test1",
name: "Test User 1",
email: "[email protected]",
priority: 2,
bookings: [
{
createdAt: new Date("2022-01-25T06:30:00.000Z"),

Choose a reason for hiding this comment

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

asdasdas

Comment on lines +1218 to +1225
id: 2,
username: "test2",
name: "Test User 2",
email: "[email protected]",
priority: 2,
bookings: [
{
createdAt: new Date("2022-01-25T05:30:00.000Z"),

Choose a reason for hiding this comment

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

wljj;ksdm,.s

lm,nasdm.as

Copy link

@mesa-dot-dev mesa-dot-dev bot left a comment

Choose a reason for hiding this comment

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

Performed full review of 3711e01...35f80a1

8 files reviewed | 1 comments | Review on Mesa | Edit Reviewer Settings


// The test doesn't accurately simulate adding the hosts as attendees,
// so we'll just check for the booker as an attendee
expect(booking?.attendees.length).toBeGreaterThanOrEqual(1);
Copy link

Choose a reason for hiding this comment

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

This test case has incomplete coverage for its stated purpose. The test is named "booking includes all selected hosts as attendees" but the comment on line 379 admits "The test doesn't accurately simulate adding the hosts as attendees". This is a critical gap in test coverage for the multiple hosts feature.

The test should either:

  1. Properly verify that all 3 selected hosts are added as attendees to the booking, or
  2. Be renamed to reflect what it actually tests

Since this is a core part of the multiple round-robin hosts feature, having proper test coverage is important for ensuring the feature works correctly.

Type: Logic | Severity: Medium

Copy link

@mesa-dev-development describe

2 similar comments
Copy link

@mesa-dev-development describe

Copy link

@mesa-dev-development describe

Copy link

@mesa-dot-dev mesa-dot-dev bot left a comment

Choose a reason for hiding this comment

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

Performed full review of 3711e01...35f80a1

Analysis

  1. Type complexity in getLuckyUser with T | T[] return types may create cognitive overhead and requires careful handling in downstream code that must accommodate both single and array returns.

  2. Performance could degrade with large host numbers since the booking flow now iterates through multiple hosts for availability checks and email processing.

  3. The dual-purpose nature of functions supporting both single and multiple host selection increases complexity and could make future maintenance more challenging.

  4. Type union patterns (T | T[]) throughout the codebase may obscure the intent of functions and require more complex type narrowing logic at call sites.

Tip

⚡ Quick Actions

This review was generated by Mesa.

Actions:

Slash Commands:

  • /review - Request a full code review
  • /review latest - Review only changes since the last review
  • /describe - Generate PR description. This will update the PR body or issue comment depending on your configuration
  • /help - Get help with Mesa commands and configuration options

8 files reviewed | 3 comments | Review on Mesa | Edit Reviewer Settings

});
const newLuckyUser = await getLuckyUser({
// Check if we need to select multiple hosts
const numberOfHostsToSelect = eventType.metadata?.multipleRoundRobinHosts || 1;
Copy link

Choose a reason for hiding this comment

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

Medium Logic

The numberOfHostsToSelect variable is declared twice - once at line 674 and again here. The second declaration shadows the first one, which could lead to confusion. Consider removing this redundant declaration.
Agent: 🔄 Zero


return luckyUser;
// Return the first N users from the ordered list
return users.slice(0, numberOfHostsToSelect) as T[];
Copy link

Choose a reason for hiding this comment

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

Medium Logic

The array slicing approach could fail if getOrderedListOfLuckyUsers returns fewer users than requested. Consider adding validation to ensure the slice operation doesn't return fewer hosts than expected, or document this edge case behavior.
Agent: 🔄 Zero


if (reassignedHost.email !== organizer.email) {
const tReassignedHost = await getTranslation(reassignedHost.locale ?? "en", "common");
if (
Copy link

Choose a reason for hiding this comment

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

Low Style

The nested array checking logic is complex and could be error-prone. Consider extracting the email comparison logic into a helper function for better readability and maintainability.
Agent: 🔄 Zero

Copy link

@mesa-dot-dev mesa-dot-dev bot left a comment

Choose a reason for hiding this comment

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

Performed full review of 3711e01...35f80a1

Analysis

  1. Return type ambiguity: The getLuckyUser function now returns either a single user or an array of users (T | T[]), creating conditional return types that must be handled carefully throughout the codebase.

  2. Increased code complexity: The dual return type pattern introduces branching logic in multiple files that need to handle both single objects and arrays, creating potential for subtle bugs.

  3. Potential performance impacts: The getOrderedListOfLuckyUsers function being used for multi-host selection may have different performance characteristics than the single-host path.

  4. Data consistency risks: The booking flow must ensure all selected hosts are properly added as attendees, have calendars updated, and receive notifications correctly.

  5. Test limitations: Heavy reliance on mocking in tests may not catch real-world integration issues between components.

Tip

⚡ Quick Actions

This review was generated by Mesa.

Actions:

Slash Commands:

  • /review - Request a full code review
  • /review latest - Review only changes since the last review
  • /describe - Generate PR description. This will update the PR body or issue comment depending on your configuration
  • /help - Get help with Mesa commands and configuration options

8 files reviewed | 5 comments | Review on Mesa | Edit Reviewer Settings

weight?: number | null;
}
>(getLuckyUserParams: GetLuckyUserParams<T>) {
>(getLuckyUserParams: GetLuckyUserParams<T>): Promise<T | T[]> {
Copy link

Choose a reason for hiding this comment

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

Medium Logic

The return type Promise<T | T[]> creates ambiguity for consumers. Consider using a more explicit approach like Promise<{ users: T[], count: number }> or overloaded function signatures to make the API clearer and safer.
Agent: 🔄 Zero

});
const newLuckyUser = await getLuckyUser({
// Check if we need to select multiple hosts
const numberOfHostsToSelect = eventType.metadata?.multipleRoundRobinHosts || 1;
Copy link

Choose a reason for hiding this comment

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

Low Style

This declaration of numberOfHostsToSelect duplicates the one at line 674. Consider extracting it to avoid redundant computation and improve maintainability.
Agent: 🔄 Zero

return (
user.email !== previousHost?.email &&
user.email !== organizer.email &&
(Array.isArray(organizer)
Copy link

Choose a reason for hiding this comment

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

High Logic

The logic is inverted here. The condition should be '!organizer.some((o) => o.email === user.email)' to exclude organizers, not include them. Currently this will include users whose email matches one of the organizers.
Agent: 🔄 Zero

)
: reassignedHost.email !== (Array.isArray(organizer) ? organizer[0].email : organizer.email)
) {
const tReassignedHost = await getTranslation(
Copy link

Choose a reason for hiding this comment

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

Medium Logic

When reassignedHost is an array, this logic assumes the array has at least one element. Add a safety check: Array.isArray(reassignedHost) && reassignedHost.length > 0 ? reassignedHost[0].locale : reassignedHost?.locale
Agent: 🔄 Zero

"common"
);
teamMembers.push({
id: reassignedHost.id,
Copy link

Choose a reason for hiding this comment

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

High Logic

The teamMembers.push() call assumes reassignedHost has id and email properties. When reassignedHost is an array, this will push an array as a single team member rather than individual members. Consider iterating over the array when reassignedHost is an array.
Agent: 🔄 Zero

Copy link

@mesa-dev-development mesa-dev-development bot left a comment

Choose a reason for hiding this comment

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

Performed full review of 3711e01...35f80a1

Analysis

  1. Type Safety Issues: The PR introduces a Promise<T | T[]> return type in core functions like getLuckyUser, requiring extensive runtime type checking (Array.isArray()) throughout the codebase. This pattern violates predictable interface principles and could lead to runtime errors.

  2. Backwards Compatibility Risks: While maintaining single-host compatibility, the implementation creates fragile contracts. The round-robin reassignment logic particularly only handles the first selected host during reassignment, potentially breaking multi-host bookings.

  3. Security Vulnerabilities: The implementation lacks proper authorization controls, validation, and bounds checking for the multipleRoundRobinHosts metadata field, creating potential attack vectors and resource exhaustion risks.

  4. Error Handling Gaps: The system lacks robust handling for edge cases like insufficient available hosts, empty result arrays, or validation failures. Functions can silently return fewer hosts than requested without indicating the shortfall.

  5. Separation of Concerns Violation: Core functions like getLuckyUser now handle both single and multiple host selection responsibilities, violating the Single Responsibility Principle and increasing complexity in testing and maintenance.

Tip

Help

Configure your agents

Mesa Docs

Slash Commands:

  • /review - Request a full code review
  • /review latest - Review only changes since the last review
  • /describe - Generate PR description. This will update the PR body or issue comment depending on your configuration
  • /help - Get help with Mesa commands and configuration options

8 files reviewed | 8 comments | Edit Agent Settings

weight?: number | null;
}
>(getLuckyUserParams: GetLuckyUserParams<T>) {
>(getLuckyUserParams: GetLuckyUserParams<T>): Promise<T | T[]> {

Choose a reason for hiding this comment

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

Medium

The return type Promise<T | T[]> creates type safety issues by forcing all consumers to perform runtime type checks. Consider using function overloads or separate functions (getLuckyUser() returning Promise<T> and getMultipleLuckyUsers() returning Promise<T[]>) to maintain type safety and clear API contracts.
Agent: 🔧 Backend


return luckyUser;
// Return the first N users from the ordered list
return users.slice(0, numberOfHostsToSelect) as T[];

Choose a reason for hiding this comment

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

Medium

The slice operation could return fewer users than numberOfHostsToSelect if insufficient users are available. This silent failure could break consuming code expecting exactly the requested number of hosts. Add validation: if (users.length < numberOfHostsToSelect) { throw new Error('Insufficient available hosts'); }
Agent: 🧠 Logic

});
const newLuckyUser = await getLuckyUser({
// Check if we need to select multiple hosts
const numberOfHostsToSelect = eventType.metadata?.multipleRoundRobinHosts || 1;

Choose a reason for hiding this comment

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

Low

This duplicates the logic from line 674. The duplicate variable declaration creates maintenance risk and potential inconsistency. Use the already-declared numberOfHostsToSelect variable instead of re-reading the metadata.
Agent: 🔧 Backend

} else {
luckyUsers.push(newLuckyUser);
// Add all lucky users
luckyUsers.push(...usersToProcess);

Choose a reason for hiding this comment

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

Medium

Using luckyUsers.push(...usersToProcess) bypasses the loop condition luckyUsers.length < numberOfHostsToSelect and could add more users than requested. This could lead to over-allocation of hosts for a booking.
Agent: 🔧 Backend

});

// Normalize reassignedRRHost to always be a single user object
const reassignedRRHost = Array.isArray(reassignedRRHostResult)

Choose a reason for hiding this comment

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

High

Critical flaw: when multiple hosts were originally selected for a booking, only the first reassigned host is used. This breaks the multi-host booking feature during reassignments. The logic should either reassign all hosts or prevent reassignment for multi-host bookings.
Agent: 🧠 Logic

user.email !== previousHost?.email &&
user.email !== organizer.email &&
(Array.isArray(organizer)
? organizer.some((o) => o.email === user.email)

Choose a reason for hiding this comment

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

High

Logic error: The condition should be !organizer.some((o) => o.email === user.email) to maintain the original filtering logic. Currently this includes users whose emails match the organizer instead of excluding them.
Agent: 🔧 Backend

giphyThankYouPage: z.string().optional(),
additionalNotesRequired: z.boolean().optional(),
disableSuccessPage: z.boolean().optional(),
multipleRoundRobinHosts: z.number().min(1).optional(), // Number of hosts to select in round-robin, defaults to 1

Choose a reason for hiding this comment

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

Medium

The validation lacks upper bounds checking, which could lead to resource exhaustion attacks where users request excessive numbers of hosts (e.g., 1000+). Add a maximum limit like .max(10) to prevent system resource abuse.
Agent: 🛡 Security

// so we'll just check for the booker as an attendee
expect(booking?.attendees.length).toBeGreaterThanOrEqual(1);

// Check that the booker email is in the attendees list

Choose a reason for hiding this comment

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

Medium

This comment reveals a critical gap: the test doesn't verify that multiple hosts are actually added as attendees to the booking, which is a core requirement. The test should validate that all selected hosts receive calendar invitations and are properly stored as attendees in the database.
Agent: 🧪 QA

Copy link

@mesa-dev-development mesa-dev-development bot left a comment

Choose a reason for hiding this comment

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

Performed full review of 3711e01...35f80a1

Tip

Help

Slash Commands:

  • /review - Request a full code review
  • /review latest - Review only changes since the last review
  • /describe - Generate PR description. This will update the PR body or issue comment depending on your configuration
  • /help - Get help with Mesa commands and configuration options

0 files reviewed | 0 comments | Edit Agent SettingsRead Docs

Copy link

@mesa-dev-development mesa-dev-development bot left a comment

Choose a reason for hiding this comment

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

Performed full review of 3711e01...35f80a1

Analysis

  1. Concurrency hazards exist if two bookings are created simultaneously, potentially oversubscribing hosts without proper transaction handling or distributed locking.

  2. Host pool scanning costs increase with the multi-host selection step, which could impact performance as the system scales.

  3. Downstream dependencies (calendar sync, email templates, webhooks) need updates to handle multiple host assignments, creating integration risks.

  4. The expanded data model and modified host assignment logic represent a significant change to the core booking flow that touches multiple system components.

  5. API backward compatibility depends on proper defaulting to a single host when the field is omitted, which if not handled correctly could break existing clients.

Tip

Help

Slash Commands:

  • /review - Request a full code review
  • /review latest - Review only changes since the last review
  • /describe - Generate PR description. This will update the PR body or issue comment depending on your configuration
  • /help - Get help with Mesa commands and configuration options

0 files reviewed | 0 comments | Edit Agent SettingsRead Docs

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.

add "light-brand" and "dark-brand" colors (add a second color picker)

6 participants