Skip to content

Conversation

@ayushjrathod-dd
Copy link

@ayushjrathod-dd ayushjrathod-dd commented Jan 5, 2026

CodeAnt-AI Description

Support assigning multiple hosts per Round Robin booking with maxRoundRobinHosts

What Changed

  • Event types gain a new maxRoundRobinHosts setting (API, OpenAPI, and DB column) that controls how many hosts are automatically assigned for a Round Robin booking; default behavior remains a single host when unset or set to 1
  • Booking flow now assigns up to the configured number of available hosts for Round Robin events (assigns all available hosts when fewer than the configured maximum)
  • UI: team event type form exposes a numeric "Maximum hosts per booking" input; docs and example usage updated
  • Added unit, end-to-end, and booking tests covering multi-host assignment scenarios

Impact

✅ Assign multiple hosts per round-robin booking
✅ Fewer booking failures when multiple hosts are available
✅ Clearer admin control over host count per booking

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

  • New Features

    • Added ability to set a maximum number of hosts assigned per round-robin booking (defaults to 1 host)
  • Documentation

    • Updated API reference and platform documentation with the new maximum hosts setting
    • Added localization strings for the feature
  • Tests

    • Added comprehensive end-to-end and unit tests for maximum hosts assignment in round-robin event types

✏️ Tip: You can customize this high-level summary in your review settings.

Add maxRoundRobinHosts config to assign up to N hosts per RR booking.
Includes schema, API v2, booking logic, UI, and tRPC integration.
Default (null/1) maintains existing single-host behavior

feat: add multi-host assignment for round robin bookings

Add maxRoundRobinHosts config to assign up to N hosts per RR booking.
Includes schema, API v2, booking logic, UI, and tRPC integration.
Default (null/1) maintains existing single-host behavior
- Add 4 unit tests covering multi-host assignment scenarios
- Update event-types-hooks.mdx with maxRoundRobinHosts documentation
- Improve API field description in DTOs for better OpenAPI docs
- Test create round robin event type with maxRoundRobinHosts
- Test default value (null) when not provided
- Test update maxRoundRobinHosts value
- Use Set for faster user lookups
- Move userIdsSet outside the loop
- Move async calls (firstUserOrgId, enrichedHosts) outside loops
- Filter group hosts once per group instead of every iteration
@codeant-ai
Copy link

codeant-ai bot commented Jan 5, 2026

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@gemini-code-assist
Copy link

Summary of Changes

Hello @ayushjrathod-dd, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request implements a significant new feature that allows round-robin event types to support multiple hosts per booking. This change provides greater control over how team members are assigned to events, moving beyond the previous single-host-per-booking model. The update involves modifications to the API, database schema, core scheduling logic, user interface, and documentation to ensure a seamless and robust experience.

Highlights

  • Multi-Host Round Robin Scheduling: Introduced a new maxRoundRobinHosts property for round-robin event types, allowing event organizers to specify the maximum number of hosts to be assigned to a single booking. This enhances flexibility for team-based scheduling.
  • API and Database Integration: The maxRoundRobinHosts property has been integrated across the API, including input and output types for creating and updating event types, and a new database migration adds the corresponding column to the EventType table. OpenAPI documentation has been updated to reflect these changes.
  • Booking Logic Enhancement: The core booking logic for round-robin events has been updated to respect the maxRoundRobinHosts setting. This ensures that when a booking is made, the system attempts to assign up to the specified number of available hosts, falling back to fewer if not enough are available.
  • User Interface and Documentation: A new input field for maxRoundRobinHosts has been added to the event type assignment tab in the UI. Additionally, relevant documentation, including platform event type hooks and localization strings, has been updated to support this new feature.
  • Comprehensive Testing: New end-to-end and unit tests have been added to validate the functionality of maxRoundRobinHosts, covering scenarios for creating and updating event types, and verifying the correct assignment of multiple hosts during the booking process.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Jan 5, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

A new maxRoundRobinHosts feature is introduced to cap the number of hosts automatically assigned per booking in Round Robin event types. Changes include database schema extension, API input/output type definitions, business logic enhancements for host selection filtering, UI form controls, localization strings, repository query updates, and comprehensive test coverage across E2E and unit tests.

Changes

Cohort / File(s) Summary
Database Schema
packages/prisma/migrations/20251208104340_add_max_round_robin_hosts_optional/migration.sql, packages/prisma/schema.prisma
Added nullable maxRoundRobinHosts integer column to EventType table in migration and schema; positioned after eventTypeColor.
API Type Definitions (v2.0)
packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts, ...update-event-type.input.ts, ...outputs/event-type.output.ts
Added optional maxRoundRobinHosts?: number field (Min: 1) with validation decorators and documentation to create/update inputs and team event type output.
API Type Definitions (v2.0 Service)
apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts
Extended Input type to include "maxRoundRobinHosts" in the permitted fields union for DatabaseEventType shape.
Organization Event Type Output Service
apps/api/v2/src/modules/organizations/event-types/services/output.service.ts
Added maxRoundRobinHosts to the DatabaseTeamEventType pick and propagated field in returned event type response object.
Event Type Repository & Data Selection
packages/features/eventtypes/repositories/eventTypeRepository.ts, packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts
Extended Prisma select statements in three repository methods (findAllByUpId, findByIdForOrgAdmin, findForSlots) to include maxRoundRobinHosts: true.
Booking Business Logic
packages/features/bookings/lib/service/RegularBookingService.ts
Refactored Round Robin host selection logic to introduce maxHostsPerGroup, excludedUserIds, and groupRRHosts filtering; tracks hosts per group and excludes previously chosen hosts to cap assignment per booking.
Form Types & Defaults
packages/features/eventtypes/lib/types.ts, packages/features/eventtypes/lib/defaultEvents.ts, packages/platform/atoms/event-types/hooks/useEventTypeForm.ts, packages/lib/test/builder.ts
Added `maxRoundRobinHosts: number
UI Form Components
packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx
Added controlled NumberInput field for maxRoundRobinHosts with validation (min 1, non-negative); refactored form component imports and strengthened onChange guards.
E2E Tests
apps/api/v2/src/modules/organizations/event-types/max-round-robin-hosts.e2e-spec.ts
New comprehensive test suite validating maxRoundRobinHosts behavior: creation with/without value, update scenarios, and host assignment verification; includes fixtures for users, teams, organizations, and platform admin setup.
Unit Tests
packages/features/bookings/lib/handleNewBooking/test/team-bookings/round-robin.test.ts
Added "Round Robin with maxRoundRobinHosts" test suite with helper functions and scenarios validating multiple/single host assignment based on maxRoundRobinHosts value (undefined, 1, or greater than 1).
Localization
apps/web/public/static/locales/en/common.json
Added two new English strings: max_round_robin_hosts_count ("Maximum hosts per booking") and max_round_robin_hosts_description.
API Documentation
docs/api-reference/v2/openapi.json
Expanded OpenAPI schema with numerous booking, calendar, and workflow-related property additions; includes documentation for future authentication requirements and detailed examples.
Developer Documentation
docs/platform/event-types-hooks.mdx
Updated useCreateTeamEventType documentation with optional maxRoundRobinHosts field (default 1) and example usage showing maxRoundRobinHosts: 2.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API as API Service
    participant Svc as OutputService
    participant DB as Database
    participant Logic as RegularBookingService

    Client->>API: POST /event-types (maxRoundRobinHosts: 2)
    API->>Svc: Validate & Save
    Svc->>DB: Create EventType with maxRoundRobinHosts
    DB-->>Svc: EventType persisted
    Svc-->>API: Response with maxRoundRobinHosts field
    API-->>Client: 201 Created

    Client->>API: POST /bookings (for Round Robin event)
    API->>Logic: Process booking (fetch eventType with maxRoundRobinHosts)
    Logic->>DB: Retrieve Round Robin event type
    DB-->>Logic: EventType {<br/>  maxRoundRobinHosts: 2,<br/>  team members: [Alice, Bob, Charlie]<br/>}
    
    rect rgb(100, 150, 200)
    Note over Logic: Apply maxRoundRobinHosts filtering
    Logic->>Logic: Select up to 2 hosts from team<br/>(excludedUserIds tracking)
    Logic->>Logic: Assign Alice & Bob<br/>(cap at maxRoundRobinHosts)
    end
    
    Logic-->>API: Booking with assigned hosts
    API-->>Client: 200 OK {attendees: [Alice, Bob]}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A bunny hops through code so bright,
Where hosts now gather, grouped just right,
Max round-robin caps the way,
Two hoppers bounce per booking day!
The feature's hopped from tail to tip,
With tests that make the logic skip!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'KAN-1 Feat/multi host round robin' is partially related to the changeset. While it references multi-host round robin functionality, the format includes a ticket identifier (KAN-1) and uses 'Feat/' prefix, making it less concise. The core concept is clear but the title could be more polished.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codeant-ai codeant-ai bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Jan 5, 2026
@refacto-visz
Copy link

refacto-visz bot commented Jan 5, 2026

Refacto PR Summary

Implemented multi-host Round Robin booking capability by adding a configurable maxRoundRobinHosts setting that allows event types to automatically assign multiple hosts per booking instead of the default single host. The feature includes database schema changes, API endpoints, UI controls, and comprehensive booking logic modifications to support scalable host assignment while maintaining backward compatibility.

Key Changes:

  • Added maxRoundRobinHosts integer column to EventType schema with API validation (minimum 1, defaults to null for single host behavior)
  • Modified Round Robin booking algorithm to assign up to the configured number of available hosts per booking, with fallback to all available hosts when fewer than maximum
  • Extended event type creation/update APIs with new field validation and OpenAPI documentation
  • Added team event type form UI with "Maximum hosts per booking" numeric input control
  • Comprehensive test coverage including E2E scenarios for multi-host assignment edge cases

Change Highlights

Click to expand
  • packages/prisma/schema.prisma: Added maxRoundRobinHosts integer column to EventType model
  • packages/features/bookings/lib/service/RegularBookingService.ts: Refactored Round Robin host selection to support multiple hosts per booking
  • packages/platform/types/event-types/event-types_2024_06_14/: Added maxRoundRobinHosts field to input/output DTOs with validation
  • packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx: Added UI control for maximum hosts configuration
  • apps/api/v2/src/modules/organizations/event-types/: Updated API services to handle new field
  • packages/features/bookings/lib/handleNewBooking/test/team-bookings/round-robin.test.ts: Added comprehensive test scenarios for multi-host assignment
  • apps/api/v2/src/modules/organizations/event-types/max-round-robin-hosts.e2e-spec.ts: End-to-end testing for API functionality
  • packages/features/eventtypes/repositories/eventTypeRepository.ts: Updated database queries to include new field

Sequence Diagram

sequenceDiagram
    participant U as User
    participant API as Booking API
    participant RR as Round Robin Service
    participant DB as Database
    participant Cal as Calendar Service
    
    U->>API: Create Round Robin booking
    API->>DB: Get event type with maxRoundRobinHosts
    DB-->>API: EventType (maxRoundRobinHosts: 3)
    API->>RR: Find available hosts
    RR->>RR: Filter by availability & groups
    RR->>RR: Select up to 3 hosts using lucky algorithm
    RR-->>API: Selected hosts [Host1, Host2, Host3]
    API->>Cal: Create calendar events for all hosts
    Cal-->>API: Calendar events created
    API->>DB: Save booking with multiple attendees
    API-->>U: Booking confirmed with 3 hosts
Loading

Testing Guide

Click to expand
  1. Multi-host assignment: Create Round Robin event type with maxRoundRobinHosts=3, add 5 team members, book slot and verify 3 hosts are assigned
  2. Fewer hosts than maximum: Set maxRoundRobinHosts=5 with only 2 available team members, confirm both hosts are assigned despite lower count
  3. Default behavior preservation: Create Round Robin event without setting maxRoundRobinHosts, verify single host assignment maintains backward compatibility
  4. API validation: Test event type creation with maxRoundRobinHosts=0 or negative values, confirm validation errors returned
  5. Recurring events: Book recurring Round Robin slots with multiple hosts, verify consistent host assignment across all instances

@refacto-visz
Copy link

refacto-visz bot commented Jan 5, 2026

Refacto is reviewing this PR. Please wait for the review comments to be posted.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the maxRoundRobinHosts feature, allowing multiple hosts to be assigned for round-robin event types. The changes are comprehensive, touching the database schema, API endpoints, UI components, and the core booking logic. E2E and unit tests have been added to cover the new functionality. My review identified a minor documentation issue and a potential bug in the booking creation logic that could prevent bookings from being made under certain conditions. Overall, the implementation is solid, but I recommend addressing the feedback to ensure correctness and clarity.

if (
[...qualifiedRRUsers, ...additionalFallbackRRUsers].length > 0 &&
luckyUsers.length !== (Object.keys(nonEmptyHostGroups).length || 1)
luckyUsers.length < (Object.keys(nonEmptyHostGroups).length || 1)

Choose a reason for hiding this comment

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

high

The condition to throw RoundRobinHostsUnavailableForBooking seems incorrect with the introduction of maxRoundRobinHosts. The current logic luckyUsers.length < (Object.keys(nonEmptyHostGroups).length || 1) appears to require at least one host from each group if groups are present, which may not be the desired behavior. A booking should likely succeed even if hosts are found in only some groups, as long as at least one host is found overall.

Consider changing this condition to luckyUsers.length === 0 to throw an error only when no hosts are available at all. This would align with the feature's goal of assigning up to a certain number of hosts.

Suggested change
luckyUsers.length < (Object.keys(nonEmptyHostGroups).length || 1)
luckyUsers.length === 0

title: "Daily standup",
slug: "daily-standup",
description: "Daily standup for the team",
schedulingType: "COLLECTIVE",

Choose a reason for hiding this comment

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

medium

The example for useCreateTeamEventType uses schedulingType: "COLLECTIVE" but also includes maxRoundRobinHosts. The documentation correctly states that maxRoundRobinHosts is for Round Robin events. To avoid confusion, the example should be updated to use schedulingType: "ROUND_ROBIN".

            schedulingType: "ROUND_ROBIN",

schedulingType: "COLLECTIVE",
hosts: [{"userId": 1456}, {"userId": 2357}],
teamId: 1234,
maxRoundRobinHosts: 2,
Copy link

Choose a reason for hiding this comment

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

Suggestion: The example adds maxRoundRobinHosts to a collective event type even though this field only has an effect for round robin scheduling, which can mislead users into thinking it affects other scheduling types as well. [possible bug]

Severity Level: Critical 🚨

Suggested change
maxRoundRobinHosts: 2,
maxRoundRobinHosts: 2, // Only add this when schedulingType is "roundRobin"
Why it matters? ⭐

This is a valid, actionable doc-improvement: the example sets schedulingType to "COLLECTIVE" but includes maxRoundRobinHosts which only matters for round-robin scheduling. That can mislead readers. Adding a short comment or moving the property behind a roundRobin example clarifies intent and avoids confusion.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** docs/platform/event-types-hooks.mdx
**Line:** 163:163
**Comment:**
	*Possible Bug: The example adds `maxRoundRobinHosts` to a collective event type even though this field only has an effect for round robin scheduling, which can mislead users into thinking it affects other scheduling types as well.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.


const { setValue, getValues, control, formState } = useFormContext<FormValues>();
const { setValue, getValues, control } = useFormContext<FormValues>();
const assignRRMembersUsingSegment = getValues("assignRRMembersUsingSegment");
Copy link

Choose a reason for hiding this comment

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

Suggestion: The value of assignRRMembersUsingSegment is read once via getValues and passed into EditWeightsForAllTeamMembers, so toggling the "filter by attributes" state will not cause RoundRobinHosts to re-render with the updated value; using useWatch for this field keeps the prop in sync with the form state and prevents stale behavior. [possible bug]

Severity Level: Critical 🚨

Suggested change
const assignRRMembersUsingSegment = getValues("assignRRMembersUsingSegment");
const assignRRMembersUsingSegment = useWatch({
control,
name: "assignRRMembersUsingSegment",
});
Why it matters? ⭐

The file currently reads assignRRMembersUsingSegment once via getValues and then passes it to EditWeightsForAllTeamMembers. That value will not update when the form field changes, causing stale prop issues and incorrect UI behavior. Switching to useWatch for that field makes RoundRobinHosts re-render with the current form state and fixes a real synchronization bug rather than being a stylistic change.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx
**Line:** 305:305
**Comment:**
	*Possible Bug: The value of `assignRRMembersUsingSegment` is read once via `getValues` and passed into `EditWeightsForAllTeamMembers`, so toggling the "filter by attributes" state will not cause `RoundRobinHosts` to re-render with the updated value; using `useWatch` for this field keeps the prop in sync with the form state and prevents stale behavior.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

)}
/>
</>
{!hostGroups.length ? (
Copy link

Choose a reason for hiding this comment

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

Suggestion: hostGroups is read with .length without a null/undefined guard, so if the form value for hostGroups is ever undefined (for instance when no groups are configured yet), this will throw a runtime error when rendering the Round Robin hosts section; guarding for a falsy hostGroups before accessing .length avoids this crash. [null pointer]

Severity Level: Minor ⚠️

Suggested change
{!hostGroups.length ? (
{!hostGroups || hostGroups.length === 0 ? (
<AddMembersWithSwitchComponent groupId={hostGroups?.[0]?.id ?? null} />
Why it matters? ⭐

In the final file hostGroups is populated with useWatch(...) and can be undefined. The current code dereferences .length without a guard which will throw if hostGroups is undefined. The suggested improved_code (guarding for falsy hostGroups) fixes a real runtime crash risk during render. This is a small, targeted safety fix and not merely cosmetic.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx
**Line:** 543:543
**Comment:**
	*Null Pointer: `hostGroups` is read with `.length` without a null/undefined guard, so if the form value for `hostGroups` is ever `undefined` (for instance when no groups are configured yet), this will throw a runtime error when rendering the Round Robin hosts section; guarding for a falsy `hostGroups` before accessing `.length` avoids this crash.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

durationLimits: null,
assignAllTeamMembers: false,
rescheduleWithSameRoundRobinHost: false,
maxRoundRobinHosts: 1,
Copy link

Choose a reason for hiding this comment

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

Suggestion: The test helper now sets maxRoundRobinHosts to 1 by default, but in the real system this field is nullable (Int? in Prisma) and other defaults (e.g. defaultEvents) use null, with the runtime logic interpreting a null/undefined value as 1 via eventType.maxRoundRobinHosts ?? 1; hardcoding 1 here makes tests build event types that no longer reflect the actual default (missing/nullable) value, which can cause tests to miss regressions around the "not provided" case and diverge from production behavior. [logic error]

Severity Level: Minor ⚠️

Suggested change
maxRoundRobinHosts: 1,
maxRoundRobinHosts: null,
Why it matters? ⭐

The Prisma schema (packages/prisma/schema.prisma) declares maxRoundRobinHosts as Int? (nullable). The default event fixtures in packages/features/eventtypes/lib/defaultEvents.ts also set maxRoundRobinHosts: null. The PR's test builder hardcodes 1 which diverges from production/default fixtures and can mask behavior when the value is omitted vs set to 1 (the application often uses eventType.maxRoundRobinHosts ?? 1). Changing the builder to use null keeps test objects faithful to production shape and avoids hiding regressions that depend on the "not provided" path.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/lib/test/builder.ts
**Line:** 143:143
**Comment:**
	*Logic Error: The test helper now sets `maxRoundRobinHosts` to `1` by default, but in the real system this field is nullable (`Int?` in Prisma) and other defaults (e.g. `defaultEvents`) use `null`, with the runtime logic interpreting a `null`/undefined value as `1` via `eventType.maxRoundRobinHosts ?? 1`; hardcoding `1` here makes tests build event types that no longer reflect the actual default (missing/nullable) value, which can cause tests to miss regressions around the "not provided" case and diverge from production behavior.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

Comment on lines +560 to +561
})
maxRoundRobinHosts?: number;
Copy link

Choose a reason for hiding this comment

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

Suggestion: The output DTO declares maxRoundRobinHosts as an optional number without null, and the OpenAPI metadata does not mark it as nullable, but the service actually returns null when the value is not set (as verified by the e2e tests and Prisma schema). This creates an API contract mismatch: generated clients and internal consumers using @calcom/platform-types or OpenAPI will think the field is either omitted or a positive integer, while they can receive null at runtime, which can cause runtime errors or incorrect handling in strictly typed or generated clients. [logic error]

Severity Level: Minor ⚠️

Suggested change
})
maxRoundRobinHosts?: number;
nullable: true,
})
maxRoundRobinHosts?: number | null;
Why it matters? ⭐

The Prisma schema defines EventType.maxRoundRobinHosts as Int? (nullable) and E2E tests explicitly assert the API returns null when the value is not set. The DTO currently declares maxRoundRobinHosts?: number (no null) and the ApiPropertyOptional metadata doesn't mark nullable: true — this creates a real API contract mismatch for generated clients and strict TypeScript consumers. Changing the property to number | null and setting nullable: true in ApiPropertyOptional fixes a runtime/typing mismatch rather than being merely cosmetic.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** packages/platform/types/event-types/event-types_2024_06_14/outputs/event-type.output.ts
**Line:** 560:561
**Comment:**
	*Logic Error: The output DTO declares `maxRoundRobinHosts` as an optional `number` without `null`, and the OpenAPI metadata does not mark it as nullable, but the service actually returns `null` when the value is not set (as verified by the e2e tests and Prisma schema). This creates an API contract mismatch: generated clients and internal consumers using `@calcom/platform-types` or OpenAPI will think the field is either omitted or a positive integer, while they can receive `null` at runtime, which can cause runtime errors or incorrect handling in strictly typed or generated clients.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

@refacto-visz
Copy link

refacto-visz bot commented Jan 5, 2026

Code Review: Multi-Host Round Robin Enhancement

PR Confidence Score: 🟨 4 / 5

👍 Well Done
Performance Optimization

Moved credential enrichment and organization lookups outside the host selection loop, preventing redundant async operations

Comprehensive Testing Coverage

Added robust E2E and unit tests covering various multi-host assignment scenarios with proper authentication

📁 Selected files for review (20)
  • apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts
  • apps/api/v2/src/modules/organizations/event-types/max-round-robin-hosts.e2e-spec.ts
  • packages/features/eventtypes/lib/defaultEvents.ts
  • packages/features/eventtypes/lib/types.ts
  • packages/features/eventtypes/repositories/eventTypeRepository.ts
  • packages/lib/test/builder.ts
  • packages/platform/atoms/event-types/hooks/useEventTypeForm.ts
  • packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts
  • packages/platform/types/event-types/event-types_2024_06_14/inputs/update-event-type.input.ts
  • packages/platform/types/event-types/event-types_2024_06_14/outputs/event-type.output.ts
  • packages/prisma/migrations/20251208104340_add_max_round_robin_hosts_optional/migration.sql
  • packages/prisma/schema.prisma
  • apps/api/v2/src/modules/organizations/event-types/services/output.service.ts
  • apps/web/public/static/locales/en/common.json
  • docs/api-reference/v2/openapi.json
  • docs/platform/event-types-hooks.mdx
  • packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts
  • packages/features/bookings/lib/handleNewBooking/test/team-bookings/round-robin.test.ts
  • packages/features/bookings/lib/service/RegularBookingService.ts
  • packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx
🎯 Custom Instructions
❌ Unapplied Instructions
Organization Guidelines

Reason: Your set path patterns [src/, config/] don't match any selected files for review; Your set extension patterns [.java] don't match any selected files for review

📝 Additional Comments
packages/features/bookings/lib/service/RegularBookingService.ts (3)
Complex Inline Multi-Host Logic

The multi-host selection logic is implemented directly within the extensive handler function, introducing nested loops and state management that increases cyclomatic complexity and violates SRP, making the core booking flow harder to test and maintain.

Standards:

  • Clean-Code-Functions
  • SOLID-SRP
  • Refactoring-Extract-Method
Per-Group Limit Multiplier Effect

The maxRoundRobinHosts limit is applied per host group rather than globally. With multiple host groups, total assigned hosts will be Groups * Limit, multiplying resource usage (DB, emails, calendar APIs) beyond expected configuration.

Standards:

  • ISO-IEC-25010-Performance-Efficiency-Resource-Utilization
  • Code-Design-Logic-Efficiency
Multi-Host Assignment Authorization

Multi-host assignment logic processes maxRoundRobinHosts value without explicit authorization checks for the requesting user's permission to assign multiple hosts. Consider adding role-based checks for multi-host booking capabilities to prevent privilege escalation.

Standards:

  • CWE-862
  • OWASP-A01
  • NIST-SSDF-PW.1
packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx (2)
Integer Overflow Protection

parseInt without bounds checking could allow extremely large values that might cause integer overflow or resource exhaustion. While HTML min=1 provides client-side validation, server-side bounds checking would prevent malicious requests.

Standards:

  • CWE-190
  • OWASP-A04
  • NIST-SSDF-PW.1
Inline Event Handler Logic

The onChange handler contains inline parsing and validation logic. Extracting this into a named handler or utility function would improve readability and keep the JSX clean.

Standards:

  • Clean-Code-Meaningful-Names
  • Clean-Code-Functions

Comment on lines 1156 to 1160
if (
[...qualifiedRRUsers, ...additionalFallbackRRUsers].length > 0 &&
luckyUsers.length !== (Object.keys(nonEmptyHostGroups).length || 1)
luckyUsers.length < (Object.keys(nonEmptyHostGroups).length || 1)
) {
throw new Error(ErrorCode.RoundRobinHostsUnavailableForBooking);
Copy link

Choose a reason for hiding this comment

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

Incomplete Group Coverage Validation

The validation luckyUsers.length < numGroups is insufficient when maxRoundRobinHosts > 1. If one group provides multiple hosts while another required group provides none, the total count might satisfy the length check despite a required group being unrepresented, violating collective scheduling requirements.

      const requiredGroups = Object.keys(nonEmptyHostGroups);
      const representedGroups = new Set();
      
      // Track which groups have assigned hosts
      for (const luckyUser of luckyUsers) {
        const userGroupId = enrichedHosts.find(host => host.user.id === luckyUser.id)?.groupId || DEFAULT_GROUP_ID;
        representedGroups.add(userGroupId);
      }
      
      if (
        [...qualifiedRRUsers, ...additionalFallbackRRUsers].length > 0 &&
        representedGroups.size < requiredGroups.length
      ) {
        throw new Error(ErrorCode.RoundRobinHostsUnavailableForBooking);
      }
Commitable Suggestion
Suggested change
if (
[...qualifiedRRUsers, ...additionalFallbackRRUsers].length > 0 &&
luckyUsers.length !== (Object.keys(nonEmptyHostGroups).length || 1)
luckyUsers.length < (Object.keys(nonEmptyHostGroups).length || 1)
) {
throw new Error(ErrorCode.RoundRobinHostsUnavailableForBooking);
const requiredGroups = Object.keys(nonEmptyHostGroups);
const representedGroups = new Set();
// Track which groups have assigned hosts
for (const luckyUser of luckyUsers) {
const userGroupId = enrichedHosts.find(host => host.user.id === luckyUser.id)?.groupId || DEFAULT_GROUP_ID;
representedGroups.add(userGroupId);
}
if (
[...qualifiedRRUsers, ...additionalFallbackRRUsers].length > 0 &&
representedGroups.size < requiredGroups.length
) {
throw new Error(ErrorCode.RoundRobinHostsUnavailableForBooking);
}
Standards
  • Business-Rule-Completeness
  • Algorithm-Correctness-Validation

Comment on lines +642 to +649
@IsInt()
@Min(1)
@IsOptional()
@DocsPropertyOptional({
description: "Only relevant for round robin event types. Specifies the maximum number of hosts to automatically assign per booking. When a booking is created, the system assigns up to this number of available hosts. If fewer hosts are available than the configured maximum, all available hosts are assigned. Minimum value is 1, defaults to 1.",
example: 1,
})
maxRoundRobinHosts?: number;
Copy link

Choose a reason for hiding this comment

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

Missing Maximum Limit Validation

Missing upper bound validation (@max) for maxRoundRobinHosts. A large value can trigger excessive synchronous external API calls (calendar syncs, emails) during booking creation, leading to request timeouts and resource exhaustion.

Standards
  • ISO-IEC-25010-Performance-Efficiency-Capacity
  • SRE-Resource-Limits

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (9)
packages/features/bookings/lib/service/RegularBookingService.ts (2)

1047-1072: Clarify semantics of maxRoundRobinHosts (per group vs per booking)

The new logic:

  • Derives maxHostsPerGroup = eventType.maxRoundRobinHosts ?? 1.
  • Loops per host group, with a per‑group hostsFoundInGroup counter.
  • Tracks excludedUserIds globally so a user is not selected twice.
  • Passes a per‑group groupRRHosts into luckyUserService.getLuckyUser.

This effectively enforces a per‑group cap while allowing up to numberOfGroups * maxRoundRobinHosts hosts per booking. However, the field name and docs describe “maximum number of hosts to assign per booking for Round Robin events”, which reads as a global cap.

If the intended behavior is truly global, you’ll likely want a totalHostsFound counter and to break outer iteration once totalHostsFound >= maxRoundRobinHosts, plus adjust the final validation that currently compares luckyUsers.length only to the number of non‑empty host groups.

If the per‑group semantics are intentional, consider documenting that explicitly (and perhaps renaming the internal variable) to avoid confusion between implementation and the public description.

Also applies to: 1081-1082, 1087-1089, 1122-1124, 1126-1127, 1133-1135, 1156-1159


1051-1059: Host enrichment with delegation credentials is wired correctly but adds another org lookup

Using getOrgIdFromMemberOrTeamId with memberId: eventTypeWithUsers.users[0]?.id and teamId: eventType.teamId to obtain orgId for enrichHostsWithDelegationCredentials, then enriching eventTypeWithUsers.hosts, is consistent with how getOrgIdFromMemberOrTeamId is used later in this file.

The extra org lookup per booking is probably acceptable, but if you see this on a hot path under high load you might want to reuse eventOrganizationId/orgId rather than re‑querying.

docs/platform/event-types-hooks.mdx (1)

133-135: Align maxRoundRobinHosts docs and example with Round Robin behavior

The text correctly describes maxRoundRobinHosts as affecting Round Robin events, but the example uses schedulingType: "COLLECTIVE" while still passing maxRoundRobinHosts: 2.

To avoid confusion for integrators, consider either:

  • Switching the example to schedulingType: "ROUND_ROBIN", or
  • Explicitly stating that maxRoundRobinHosts is only honored for Round Robin scheduling types and ignored otherwise.

Also applies to: 155-164

apps/api/v2/src/modules/organizations/event-types/services/output.service.ts (1)

102-144: Properly wired maxRoundRobinHosts through the response

Destructuring maxRoundRobinHosts from databaseEventType and returning it alongside rescheduleWithSameRoundRobinHost cleanly exposes the new field on the team event type response. Behavior remains backward compatible since the field is optional.

If you ever revisit this function, consider grouping “RR-related” flags (rescheduleWithSameRoundRobinHost, maxRoundRobinHosts, distribution settings) in a small nested object to reduce top-level clutter, but this is non-blocking.

apps/api/v2/src/modules/organizations/event-types/max-round-robin-hosts.e2e-spec.ts (1)

83-88: Consider removing the @ts-ignore comment by using a type assertion.

The @ts-ignore suppresses type checking but masks potential issues. Consider using a proper type assertion or casting instead.

🔎 Suggested fix
     const body: CreateTeamEventTypeInput_2024_06_14 = {
       title: "Round Robin Event",
       slug: `max-rr-hosts-${randomString()}`,
       lengthInMinutes: 30,
-      // @ts-ignore - schedulingType accepts string
-      schedulingType: "roundRobin",
+      schedulingType: "roundRobin" as unknown as keyof typeof import("@calcom/prisma/enums").SchedulingType,
       assignAllTeamMembers: true,
       ...overrides,
     };

Alternatively, verify if the input type definition can be updated to accept the string literal directly.

packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx (1)

517-541: Consider adding a visual indicator for the default behavior when the field is empty.

The input correctly handles null values and validates that the number is at least 1. However, the UX might be confusing since:

  • An empty field means null, which defaults to assigning a single host
  • The placeholder shows "1" but doesn't explicitly communicate this is the default behavior

The description at line 525 (t("max_round_robin_hosts_description")) should clarify this, but consider adding helper text or adjusting the placeholder.

🔎 Potential improvement
               <NumberInput
                 min={1}
-                placeholder="1"
+                placeholder={t("default_one")}
                 className="w-24"
                 value={value ?? ""}
                 onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                   const val = e.target.value === "" ? null : parseInt(e.target.value, 10);
                   if (val === null || (!isNaN(val) && val >= 1)) {
                     onChange(val);
                   }
                 }}
               />
+              <p className="text-muted text-xs mt-1">{t("leave_empty_for_default")}</p>

This would require adding appropriate localization strings.

docs/api-reference/v2/openapi.json (3)

19667-19670: Use "type": "integer" with a minimum constraint.

Since maxRoundRobinHosts represents a discrete count of hosts, it should be typed as "integer" rather than "number". Additionally, the description states "Minimum value is 1" but the schema lacks the corresponding minimum constraint for validation.

🔎 Proposed fix
          "maxRoundRobinHosts": {
-            "type": "number",
+            "type": "integer",
+            "minimum": 1,
             "description": "Only relevant for round robin event types. Specifies the maximum number of hosts to automatically assign per booking. When a booking is created, the system assigns up to this number of available hosts. If fewer hosts are available than the configured maximum, all available hosts are assigned. Minimum value is 1, defaults to 1.",
             "example": 1
           }

21685-21688: Apply the same fix: use "type": "integer" with minimum: 1.

Same issue as the previous occurrence.

🔎 Proposed fix
          "maxRoundRobinHosts": {
-            "type": "number",
+            "type": "integer",
+            "minimum": 1,
             "description": "Only relevant for round robin event types. Specifies the maximum number of hosts to automatically assign per booking. When a booking is created, the system assigns up to this number of available hosts. If fewer hosts are available than the configured maximum, all available hosts are assigned. Minimum value is 1, defaults to 1.",
             "example": 1
           }

22153-22156: Apply the same fix: use "type": "integer" with minimum: 1.

Same issue as the previous occurrences.

🔎 Proposed fix
          "maxRoundRobinHosts": {
-            "type": "number",
+            "type": "integer",
+            "minimum": 1,
             "description": "Only relevant for round robin event types. Specifies the maximum number of hosts to automatically assign per booking. When a booking is created, the system assigns up to this number of available hosts. If fewer hosts are available than the configured maximum, all available hosts are assigned. Minimum value is 1, defaults to 1.",
             "example": 1
           }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f350542 and 11315b8.

📒 Files selected for processing (20)
  • apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts
  • apps/api/v2/src/modules/organizations/event-types/max-round-robin-hosts.e2e-spec.ts
  • apps/api/v2/src/modules/organizations/event-types/services/output.service.ts
  • apps/web/public/static/locales/en/common.json
  • docs/api-reference/v2/openapi.json
  • docs/platform/event-types-hooks.mdx
  • packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts
  • packages/features/bookings/lib/handleNewBooking/test/team-bookings/round-robin.test.ts
  • packages/features/bookings/lib/service/RegularBookingService.ts
  • packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx
  • packages/features/eventtypes/lib/defaultEvents.ts
  • packages/features/eventtypes/lib/types.ts
  • packages/features/eventtypes/repositories/eventTypeRepository.ts
  • packages/lib/test/builder.ts
  • packages/platform/atoms/event-types/hooks/useEventTypeForm.ts
  • packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts
  • packages/platform/types/event-types/event-types_2024_06_14/inputs/update-event-type.input.ts
  • packages/platform/types/event-types/event-types_2024_06_14/outputs/event-type.output.ts
  • packages/prisma/migrations/20251208104340_add_max_round_robin_hosts_optional/migration.sql
  • packages/prisma/schema.prisma
🧰 Additional context used
🧬 Code graph analysis (3)
packages/features/bookings/lib/handleNewBooking/test/team-bookings/round-robin.test.ts (6)
apps/web/test/utils/bookingScenario/bookingScenario.ts (10)
  • getBooker (2260-2274)
  • getOrganizer (1543-1608)
  • TestData (1262-1534)
  • getGoogleCalendarCredential (1215-1223)
  • Timezones (317-321)
  • createBookingScenario (1002-1033)
  • getScenarioData (1610-1698)
  • mockSuccessfulVideoMeetingCreation (2115-2133)
  • mockCalendarToHaveNoBusySlots (1999-2012)
  • getDate (1116-1163)
packages/features/bookings/lib/handleNewBooking/test/complex-schedules.test.ts (1)
  • Timezones (33-36)
packages/features/bookings/lib/handleNewBooking/test/date-overrides.test.ts (1)
  • Timezones (33-36)
packages/platform/libraries/index.ts (1)
  • SchedulingType (29-29)
packages/features/bookings/lib/handleNewBooking/test/getNewBookingHandler.ts (1)
  • getNewBookingHandler (15-17)
apps/web/test/utils/bookingScenario/getMockRequestDataForBooking.ts (1)
  • getMockRequestDataForBooking (42-56)
apps/api/v2/src/modules/organizations/event-types/max-round-robin-hosts.e2e-spec.ts (3)
packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts (1)
  • CreateTeamEventTypeInput_2024_06_14 (566-650)
packages/platform/types/event-types/event-types_2024_06_14/outputs/event-type.output.ts (1)
  • TeamEventTypeOutput_2024_06_14 (494-562)
packages/platform/types/event-types/event-types_2024_06_14/inputs/update-event-type.input.ts (1)
  • UpdateTeamEventTypeInput_2024_06_14 (481-564)
packages/features/bookings/lib/service/RegularBookingService.ts (5)
packages/lib/getOrgIdFromMemberOrTeamId.ts (1)
  • getOrgIdFromMemberOrTeamId (47-62)
packages/app-store/delegationCredential.ts (1)
  • enrichHostsWithDelegationCredentials (425-465)
packages/lib/constants.ts (1)
  • DEFAULT_GROUP_ID (226-226)
packages/lib/CalEventParser.ts (1)
  • getVideoCallUrlFromCalEvent (427-444)
packages/platform/atoms/prisma-types/index.ts (1)
  • JsonValue (5-5)
🔇 Additional comments (35)
packages/lib/test/builder.ts (1)

85-170: Default maxRoundRobinHosts in test builder is consistent with service logic

Setting maxRoundRobinHosts: 1 in buildEventType matches the runtime fallback (eventType.maxRoundRobinHosts ?? 1) and gives deterministic test fixtures. Looks good.

packages/features/bookings/lib/service/RegularBookingService.ts (7)

39-39: Async booking email/SMS tasker wiring looks sound

Injecting BookingEmailAndSmsTasker via IBookingServiceDependencies and deferring to it under ENABLE_ASYNC_TASKER with a bookingEmailsAndSmsTaskerAction derived from the actual flow (requested/confirmed/rescheduled) is consistent and side‑effect free for dry runs or when the feature flag/team feature is off. No issues spotted.

Also applies to: 459-469, 531-531, 2671-2695


1315-1321: Static conferencing URLs: avoiding credentials looks correct

Branching to:

const { bookingLocation, conferenceCredentialId } =
  organizerOrFirstDynamicGroupMemberDefaultLocationUrl
    ? { bookingLocation: organizerOrFirstDynamicGroupMemberDefaultLocationUrl, conferenceCredentialId: undefined }
    : getLocationValueForDB(...);

ensures static URLs (pre‑resolved links) are treated as plain locations and don’t try to use conferencing credentials. The updated destinationCalendar fallback to organizer’s destination calendar if the event type doesn’t specify one also preserves expected behavior.

Also applies to: 1363-1367


1986-1995: New EventManager.reschedule call signature

The extended eventManager.reschedule call now passes changedOrganizer, previousHostDestinationCalendar, isBookingRequestedReschedule, and skipDeleteEventsAndMeetings, which matches the nearby logic that computes these flags and inputs.

Assuming EventManager.reschedule has been updated to accept this expanded signature in its own module, this looks consistent.


2293-2297: Video call URL handling now consistently prefers the canonical event URL

Using:

const metadata = videoCallUrl
  ? { videoCallUrl: getVideoCallUrlFromCalEvent(evt) || videoCallUrl }
  : undefined;

and then threading metadata.videoCallUrl into both the booking response and stored booking metadata unifies how the video URL is derived (videoCallData → integration hangoutLink → HTTP location → fallback). This should reduce drift between what’s actually on the calendar event and what’s exposed to consumers.

Also applies to: 2465-2467, 2581-2583


2308-2316: oldBooking payload addition for reschedules

Including an oldBooking object with startTime and endTime in BookingRescheduledPayload is a useful addition for downstream handlers/auditing.

Just ensure the BookingRescheduledPayload type in onBookingEvents is updated accordingly (with oldBooking optional) so there’s no type drift.


2391-2393: Casting eventType.metadata.apps to Prisma.JsonValue before payment

The change:

metadata: eventType.metadata
  ? { ...eventType.metadata, apps: eventType.metadata?.apps as Prisma.JsonValue }
  : {},

is a reasonable way to satisfy Prisma’s JsonValue typing for apps without mutating the original metadata object. This should help keep the payment flow type‑safe.


2727-2754: RegularBookingService constructor & overloads unchanged in behavior

The DI constructor and the three public methods (createBooking, rescheduleBooking, createBookingForApiV1) remain thin wrappers over handler, just wiring through bookingMeta and an optional schema getter. No behavioral changes beyond the internal handler updates.

packages/prisma/migrations/20251208104340_add_max_round_robin_hosts_optional/migration.sql (1)

1-2: Migration for maxRoundRobinHosts is straightforward

Adding a nullable INTEGER column maxRoundRobinHosts to "public"."EventType" aligns with the Prisma model (Int?) and service‑level defaulting. No issues here.

packages/features/eventtypes/lib/types.ts (1)

84-182: FormValues maxRoundRobinHosts field matches backend shape

Adding maxRoundRobinHosts: number | null to FormValues aligns with the Prisma Int? field and the rest of the PR (defaults set via defaultEvents). As long as the form always initializes this to either null or a positive integer, this type is appropriate.

packages/prisma/schema.prisma (1)

245-252: EventType.maxRoundRobinHosts model field is consistent with migration

Defining maxRoundRobinHosts Int? on EventType matches the migration and how the service code handles it (?? 1). Any bounds (e.g., enforcing >= 1) are correctly left to higher‑level validation (zod / API layer), so nothing else is needed in the schema.

packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts (1)

18-89: Selecting maxRoundRobinHosts for booking flow is correct

Adding maxRoundRobinHosts: true to getEventTypesFromDBSelect ensures the booking flow sees this field and can apply the new round‑robin logic. The rest of the transformation already spreads all scalar fields via restEventType, so this wires through cleanly.

packages/platform/atoms/event-types/hooks/useEventTypeForm.ts (1)

122-122: LGTM! Field correctly integrated into form defaults.

The maxRoundRobinHosts field is properly added to the form's default values, sourced from eventType.maxRoundRobinHosts. The placement alongside other round-robin configuration fields is logical.

packages/platform/types/event-types/event-types_2024_06_14/inputs/create-event-type.input.ts (1)

642-649: LGTM! Input validation is properly configured.

The field validation is consistent with the output type. The @Min(1) validator correctly ensures that when a value is provided, it must be at least 1, while @IsOptional() allows the field to be omitted entirely.

apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts (1)

97-97: LGTM! Type extension for team event type support.

Adding maxRoundRobinHosts to the Input type extends the DatabaseEventType interface to support this field system-wide. While this service's getResponseEventType method returns EventTypeOutput_2024_06_14 (which doesn't include this field), the type extension ensures the field is available for team event type services that return TeamEventTypeOutput_2024_06_14.

packages/platform/types/event-types/event-types_2024_06_14/inputs/update-event-type.input.ts (1)

556-563: LGTM! Update input consistent with create input.

The field definition matches the create input type, maintaining consistency across the API surface. The validation correctly allows the field to be omitted (optional) or provided with a minimum value of 1.

apps/api/v2/src/modules/organizations/event-types/services/output.service.ts (1)

32-92: Input type extension for maxRoundRobinHosts looks correct

Including maxRoundRobinHosts in the Input pick keeps this service aligned with the EventType model and other plumbing in the PR. No issues from a typing or usage perspective.

packages/features/eventtypes/lib/defaultEvents.ts (1)

63-156: Sane default for maxRoundRobinHosts in commons

Adding maxRoundRobinHosts: null to commons keeps the default event shape in sync with the new field while deferring the actual defaulting (to 1) to business logic/UI. This is consistent with the rest of the event defaults.

apps/web/public/static/locales/en/common.json (1)

4108-4110: New locale strings for max round robin hosts are clear and consistent

The label and description accurately describe the feature (“Maximum hosts per booking”, defaulting to 1) and follow existing copy style. No i18n issues spotted.

packages/features/bookings/lib/handleNewBooking/test/team-bookings/round-robin.test.ts (2)

1089-1170: Well-structured test helpers for maxRoundRobinHosts scenarios.

The helper functions (createBooker, createOrganizer, createTeamMember, createTeamMembers, setupScenario, createBooking) effectively reduce code duplication and improve test readability. The conditional spread on line 1139 correctly handles the optional maxRoundRobinHosts parameter.


1191-1236: Good test coverage for maxRoundRobinHosts core scenarios.

The tests cover the essential cases:

  • Assigning exactly maxRoundRobinHosts when enough hosts are available
  • Gracefully handling fewer available hosts than the max
  • Default behavior when maxRoundRobinHosts is undefined
  • Explicit maxRoundRobinHosts: 1 behavior

Consider adding a test for the scenario where maxRoundRobinHosts is set but some hosts are busy (existing bookings), to verify the system correctly assigns only available hosts up to the max.

apps/api/v2/src/modules/organizations/event-types/max-round-robin-hosts.e2e-spec.ts (2)

207-230: Good E2E coverage for create and update scenarios.

The tests validate:

  • Setting maxRoundRobinHosts on creation
  • Default null when not provided
  • Updating the value

Consider adding a test to verify that maxRoundRobinHosts can be cleared (set back to null) after being set, to ensure the full update lifecycle is covered.


199-205: No changes needed. The cleanup implementation is correct—cascade deletion is properly configured in the Prisma schema to handle all dependent entities when the organization is deleted, including managedTeam and all event types created during tests.

Likely an incorrect or invalid review comment.

packages/features/eventtypes/repositories/eventTypeRepository.ts (3)

606-606: Correctly added maxRoundRobinHosts to the event type select.

This ensures the field is included when fetching event type data for editing and management.


905-905: Consistent with findById - org admin access includes maxRoundRobinHosts.

This mirrors the change in findById, ensuring organization admins have access to the same data.


1265-1265: Essential for slot calculation logic.

Including maxRoundRobinHosts in findForSlots is necessary for the round-robin host selection algorithm to respect the configured maximum hosts per booking.

packages/features/eventtypes/components/tabs/assignment/EventTeamAssignmentTab.tsx (5)

30-30: LGTM - Import consolidation.

Adding NumberInput to the existing import from @calcom/ui/components/form keeps imports organized.


104-110: Defensive null check for onChange.

The explicit if (onChange) guard prevents potential runtime errors when onChange is not provided, making the component more robust.


282-282: Unused parameter prefixed with underscore.

Prefixing orgId with _ indicates it's intentionally unused while maintaining the interface contract. This follows common conventions for signaling unused parameters.


752-752: Simplified conditional is cleaner.

Removing the double-negation (!!) from the truthiness check improves readability without changing behavior.


918-921: Clearer conditional logic for tooltip content.

The direct evaluation of eventType.team?.rrTimestampBasis && eventType.team?.rrTimestampBasis !== RRTimestampBasis.CREATED_AT is more readable than the previous double-negation approach.

docs/api-reference/v2/openapi.json (4)

13844-13844: LGTM!

The enhanced description clearly documents the slot format options, seated slot properties, and empty response behavior.


14455-14455: LGTM!


29460-29463: LGTM!

Good practice to proactively notify API consumers about upcoming authentication requirements.


29491-29494: LGTM!

Comment on lines +554 to +561
@IsInt()
@Min(1)
@IsOptional()
@ApiPropertyOptional({
description: "Only relevant for round robin event types. Specifies the maximum number of hosts to automatically assign per booking. When a booking is created, the system assigns up to this number of available hosts. If fewer hosts are available than the configured maximum, all available hosts are assigned. Minimum value is 1, defaults to 1.",
example: 1,
})
maxRoundRobinHosts?: number;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Check Prisma schema for default value
echo "=== Checking Prisma schema for maxRoundRobinHosts default ==="
rg "maxRoundRobinHosts" packages/prisma/schema.prisma -A 2 -B 2

# Check service layer for default value handling
echo -e "\n=== Checking service layer for default value handling ==="
find apps -type f -name "*.ts" | xargs rg "maxRoundRobinHosts" -B 3 -A 3

Repository: coderabbit-test/cal.com

Length of output: 7486


Fix misleading documentation about default value.

The description states "defaults to 1", but the Prisma schema has no default value defined for this field (maxRoundRobinHosts Int?), and e2e tests confirm the field returns null when not provided. Update the description to accurately reflect that the field is optional and defaults to null when not set, or implement an actual database-level default if that was the intent.

@codeant-ai
Copy link

codeant-ai bot commented Jan 5, 2026

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • HostGroups undefined
    hostGroups is read from useWatch and can be undefined. The code uses expressions like !hostGroups.length, hostGroups.length, and indexing (hostGroups[0]) which will throw at runtime when hostGroups is undefined. Ensure a safe default (e.g. []) or use optional chaining before accessing .length or indices.

  • Missing validation
    maxRoundRobinHosts is pulled from the DB and returned in the response without validation or normalization. If the DB value is non-numeric, negative, fractional, or extremely large it may cause unexpected behavior downstream (UI or booking assignment logic). Review and add normalization/clamping and ensure API/DB contracts are enforced.

  • Type/value validation
    The DB field value may be null/undefined or an unexpected type. Any business logic that uses maxRoundRobinHosts (e.g., round-robin assignment) should validate and coerce the value to a safe numeric range (min 1) to avoid logic bugs or runtime errors.

  • Input Coercion
    The new numeric property uses @ISINT() and @IsOptional() but there is no class-transformer coercion (e.g. @type(() => Number) or Transform) to convert incoming string values to numbers. Requests coming from JSON or form-urlencoded inputs might fail validation when the field is sent as a string.

  • Input transformation risk
    @IsInt() validation expects a numeric type. If the incoming JSON sends this value as a string (common from some clients), validation will fail unless the value is transformed to a number. Consider adding a transformer (e.g. @Type(() => Number) or @Transform) so string inputs like "2" validate correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants