Blitzy: Fix orphaned buffer time events in seated booking reschedule and last-attendee-leaves flows (CI-002 gap closure)#5
Merged
blitzy[bot] merged 17 commits intomainfrom Mar 23, 2026
Conversation
…ncellation (CI-002 gap) When an owner reschedules a seated booking to a time slot that already has another booking (merge flow), the source booking is marked CANCELLED but its buffer events were left orphaned in the external calendar. Changes: - Add CalendarEvent type import from @calcom/types/Calendar - Insert buffer event cleanup block after the source booking is cancelled: * Dynamically imports BufferTimeEventService, CredentialRepository, getCalendar * Checks calendar-buffer-sync feature flag via isBufferSyncEnabled() * Queries bookingReference for buffer_time_* refs on the cancelled booking * Deletes each buffer event from the external calendar via adapter * Marks each BookingReference as deleted * Best-effort error handling (try/catch per reference + outer try/catch) - Does NOT modify the eventManager.reschedule() call (line 126) to avoid creating duplicate buffer events on the target booking Follows patterns from EventManager.ts:1547-1633 (deleteBufferEventsForBooking)
…NewTimeSlot reschedule call CI-002 gap closure: When owner reschedules a seated booking to a new time slot, EventManager.reschedule() was called without bufferContext (8th argument), causing orphaned buffer events in external calendars when syncBuffersToCalendar is enabled. Changes: - Import BufferEventContext type from EventManager - Build buffer context from eventType and organizerUser data when syncBuffersToCalendar is truthy (matching RegularBookingService pattern) - Pass bufferCtx as 8th argument to eventManager.reschedule() to enable buffer event delete-and-recreate lifecycle on seated booking reschedule When syncBuffersToCalendar is falsy/null/undefined, bufferCtx is undefined and no buffer operations occur (zero regression to existing flows).
…eeDeleteBooking
CI-002 gap closure: When the last attendee leaves a seated booking,
the reference cleanup loop now handles buffer_time_before and
buffer_time_after references in addition to _video and _calendar types.
Previously, buffer event references were silently skipped because the
loop only checked for _video and _calendar type patterns. This left
orphaned buffer events in external calendars when syncBuffersToCalendar
was enabled and the calendar-buffer-sync feature flag was active.
The fix adds a new conditional block after the _calendar handling that:
- Matches references with type.startsWith('buffer_time')
- Guards with originalBookingEvt (same as _calendar block)
- Uses getCalendar(credential, 'booking') to obtain the adapter
- Calls calendar.deleteEvent() to remove the external calendar event
- Pushes the deletion promise to integrationsToDelete for batch execution
The fix is a no-op when:
- syncBuffersToCalendar is false (no buffer references exist)
- calendar-buffer-sync flag is disabled (no buffer references created)
- originalBookingEvt is undefined (guard prevents execution)
…dBookings buffer cleanup - Extract buffer cleanup logic into dedicated deleteBufferEventsForCancelledBooking() helper to bring combineTwoSeatedBookings under Biome noExcessiveLinesPerFunction threshold (INFO) - Move prisma.bookingReference.update soft-delete outside if(ref.uid) guard to match EventManager.ts:1610-1614 reference pattern — ensures references without UIDs are still soft-deleted defensively (MINOR) Both changes follow the CI-002 gap closure patterns established in EventManager.ts.
…re in seated bookings Add comprehensive test coverage for buffer event lifecycle management in seated booking flows, verifying the three CI-002 gap closure bug fixes: Bug 1 - moveSeatedBookingToNewTimeSlot: Verify bufferContext is passed as 8th argument to eventManager.reschedule() when syncBuffersToCalendar=true, and is undefined when syncBuffersToCalendar=false. Bug 2 - combineTwoSeatedBookings: Verify source booking buffer events are cleaned up after merge cancellation, and that no duplicate buffer events are created on the target booking (8th arg to reschedule is undefined). Bug 3 - lastAttendeeDeleteBooking: Verify buffer_time_before and buffer_time_after references are processed in the cleanup loop and calendar.deleteEvent() is called for each, and that no buffer-related deleteEvent calls occur when no buffer references exist. All 6 new tests pass alongside the existing 20 seated booking tests. No existing tests or assertions were modified.
…gap closure - Test 3 (merge flow): Replace vacuous toBeGreaterThanOrEqual(0) with calendar mock setup and UID presence assertions verifying both buffer-before-uid and buffer-after-uid are deleted (MAJOR finding) - Test 5 (last attendee): Replace weak toBeGreaterThanOrEqual(1) with toBeGreaterThanOrEqual(2) and individual UID presence checks (MINOR) - Add new test: verify merge flow skips buffer cleanup when calendar-buffer-sync feature flag is disabled (INFO finding) All 27 seated booking tests pass. Zero new compilation errors, zero new lint violations, zero regressions.
…n updateEvent result for buffer event creation on reschedule (CI-002 gap) Root cause: CalendarManager.updateEvent() did not return credentialId, delegatedToId, or externalId in its EventResult, while createEvent() did. This caused EventManager.createBufferEventsForBooking() to fail credential resolution during non-seated booking reschedule, silently skipping buffer event creation at the rescheduled time. Fix: Add the three missing fields to updateEvent's return object, matching createEvent's return shape. This enables the buffer event credential lookup in EventManager to succeed after an updateAllCalendarEvents() call. Tests: 7 new test cases in CalendarManager.test.ts verify updateEvent now returns credentialId, delegatedToId, and externalId in all scenarios (success, failure, delegation, null externalCalendarId).
…nd cancellation
Root cause: BaseCalendarService.deleteEvent() used string concatenation for
CalDAV object URL construction (`${cal.externalId}${uid}.ics`), which fails
when the calendar URL lacks a trailing slash. tsdav's createCalendarObject uses
`new URL(filename, calendar.url).href` which correctly resolves relative URLs.
Fix 1: Update getEventsByUID() to use `new URL(`${uid}.ics`, calendarUrl)`
for consistent URL construction matching tsdav's creation path.
Fix 2: Accept optional externalCalendarId in deleteEvent() for direct CalDAV
object URL construction. Buffer events store externalCalendarId in
BookingReference, enabling direct deletion without expensive listCalendars().
Falls back to full calendar search when direct lookup returns empty.
Includes 5 new test cases covering:
- Direct buffer event deletion via externalCalendarId
- Fallback to full calendar search
- URL construction for calendar URLs without trailing slash
- Buffer event deletion on reschedule (before + after events)
- Buffer event deletion on cancellation (last attendee leaves)
- BaseCalendarService.createEvent accepts externalCalendarId as 3rd param, using it to filter which calendar the event is created on (takes priority over destinationCalendar lookup). Fixes partial failures when iCloud/CalDAV buffer events are created on ALL user calendars (including read-only ones). - CalendarManager.createEvent passes externalId to calendar.createEvent() for all credentials, not only delegation credentials. Previously the externalCalendarId was gated behind delegatedToId check, so non-delegation Apple Calendar credentials never received the target calendar. - Updated bidirectionalSync test to match new behavior: externalId is now forwarded to adapters regardless of delegation status. - Added 4 test cases to CalendarService.test.ts covering Apple Calendar externalCalendarId targeting, priority over destinationCalendar, and read-only calendar avoidance. - Added 3 test cases to bufferTimeVisualization.test.ts covering Apple Calendar destination targeting in buffer event creation flows.
blitzy bot
pushed a commit
that referenced
this pull request
Mar 27, 2026
…dation, nested form hydration - CheckedTeamSelect.tsx: Remove Fragment wrapper around <li> in .map() to fix React 'key prop' console warning (Issue #1 MINOR) - EditWeightsForAllTeamMembers.tsx: Add parseAndClampWeight() helper enforcing integer >= 0 and <= 999 for weight inputs; replace onBlur/onKeyDown handlers with handleWeightCommit() using clamped validation (Issue #2 MINOR) - EditWeightsForAllTeamMembers.tsx: Replace nested <form> with <div> to eliminate 'form cannot be descendant of form' hydration warning (Issue #5 INFO) - HostEditDialogs.tsx: Fix setWeight() guard from '!!newWeight' to 'newWeight !== undefined && !isNaN(newWeight)' to allow weight=0 assignment; add validation/clamping in onChange handler (Issue #2 secondary)
blitzy bot
pushed a commit
that referenced
this pull request
Mar 27, 2026
…ation - CRITICAL: Add @UseGuards(ApiAuthGuard, RolesGuard) and @roles('TEAM_MEMBER') to GET /v2/teams/:teamId/event-types list endpoint, closing unauthenticated data exposure vulnerability (Issue #1) - MAJOR: Strip stack traces from tRPC error responses in non-development environments via defense-in-depth check in errorFormatter (Issue #4) - MAJOR: Replace CORS wildcard with env-based ALLOWED_ORIGINS configuration; defaults to blocked in production, wildcard only in development (Issue #5) - MAJOR: Add HSTS, CSP, X-Frame-Options security headers to web app and suppress X-Powered-By via poweredByHeader: false (Issue #6) - MINOR: Add bidirectional Zod .refine() validation ensuring team-only schedulingTypes (ROUND_ROBIN, COLLECTIVE, MANAGED) require teamId (Issue #2) - MINOR: Add @min(1)/@max(1000) bounds to hostsLimit query parameter in GetTeamEventTypesQuery DTO (Issue #3)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes three distinct bugs where buffer time events persisted as orphans in external calendars during seated booking lifecycle flows. The root cause was that the seated booking subsystem (
packages/features/bookings/lib/handleSeats/) was never updated to participate in buffer event lifecycle management when the CI-002 gap closure (buffer time visualization) was implemented.Bug Fixes
Bug 1 — Missing Buffer Context in Owner Reschedule (Move to New Time Slot)
File:
moveSeatedBookingToNewTimeSlot.tsBufferEventContextfromeventTypeandorganizerUserwhensyncBuffersToCalendaris truthyeventManager.reschedule(), enabling the buffer delete/create block atEventManager.ts:811Bug 2 — Missing Buffer Cleanup in Owner Reschedule (Merge)
File:
combineTwoSeatedBookings.tsdeleteBufferEventsForCancelledBooking()helper using dynamic imports forBufferTimeEventServiceandCredentialRepositoryBug 3 — Buffer References Skipped in Last Attendee Departure
File:
lastAttendeeDeleteBooking.tsreference.type.startsWith("buffer_time")condition in the reference cleanup loop_videoand_calendarreferencesAdditional Fixes (Discovered During Validation)
BaseCalendarService.createEventnow acceptsexternalCalendarIdto target specific calendars, preventing partial failures on read-only CalDAV calendarsexternalIdforwarded for all credentials, not just delegation credentialscredentialId,delegatedToId,externalIdto update result for buffer event creation on rescheduleURLconstructor ingetEventsByUIDto correctly resolve.icsfilenames against calendar URLs regardless of trailing slashTest Results
handleSeats.test.tscovering all seated booking buffer flowsbufferTimeVisualization.test.tsCalendarManager.test.tsCalendarService.test.tsFeature Flag Safety
All fixes are inert when either
calendar-buffer-syncflag is disabled orsyncBuffersToCalendaris false on the event type. No behavioral change for non-buffer flows.