Skip to content

fix(booking): scheduling-timezone limit buckets, guarded + idempotent allocation, requested-path parity - #998

Merged
teetangh merged 4 commits into
devfrom
fix/booking-algorithm-calendar
Jul 17, 2026
Merged

fix(booking): scheduling-timezone limit buckets, guarded + idempotent allocation, requested-path parity#998
teetangh merged 4 commits into
devfrom
fix/booking-algorithm-calendar

Conversation

@teetangh

@teetangh teetangh commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Problem

The Allocate Slots dialog (RequestCalendar) is the booking surface for consultants and consultees, and its verdicts did not match the server's. Concretely:

  1. Three divergent week definitions on the client vs the server. The hook bucketed weekly limits with local getDay() mixed with UTC ISO keys, UnifiedCalendar used date-fns startOfWeek (browser-local), and only a few paths used the canonical SlotCalculationService. The server bucketed by UTC Sundays for weekly limits but by server-local toDateString() for per-day caps. For slots between 00:00 and 05:30 IST the calendar accepted selections the server rejected with WEEKLY_LIMIT/DAILY_LIMIT errors — and vice versa.
  2. Multi-tab races were unguarded. No client path sent an Idempotency-Key (the [UMBRELLA] B2C state-machine and payments hardening — launch blockers + first-month #837 server replay path was dead code). Worse, auto-allocation locks auto-allocate:{consultantId} while manual allocation day-shards its lock key ([ALLOCATION] Narrow the consultant-level auto-allocate lock — now unblocked by the #440 exclusion constraint #860) — different Redis keys — so a consultant with the same request open in two tabs could run manual in one and auto in the other concurrently, and the manual path treated the already-allocated event as a legal re-allocation and silently deleted and replaced the winner's appointments. Group events (webinar/class) have no consultee lock at all.
  3. The requested-slots path (Use Requested Times) ignored the plan's totalSessions and pastConfirmedSlotCount, so it rejected valid in-progress reschedules that manual/auto accepted.
  4. A callsPerWeek × 4 × durationInMonths fallback assumed 4-week months — wrong for 28/29/30/31-day periods spanning 5–6 Sunday weeks, and moot anyway because the server throws for a subscription with no scheduling period.
  5. Same-tick validation toasts clobbered each other (single pendingToast slot), and wording drifted between the week view, month view, and requests table.

Approach

ADR B9 — limits bucket by the event's scheduling timezone; display stays viewer-local. Every daily/weekly limit (subscription callsPerWeek weeks, 1-session/day, 2-classes/day, consultation same-day) now buckets through two shared helpers, SlotCalculationService.dayKey(date, tz) and weekKey(date, tz), where the timezone is the event's schedulingTimezone column (default Asia/Kolkata). The keys are produced by Intl with an explicit zone — never toDateString() or the process locale — so the client's interactive guards, the client auto-allocator, and the server validators (SlotValidationService, SubscriptionValidationService, the server auto-allocator) all agree on every machine. For the primary Indian market, "one session per day" now means exactly the calendar day users see on the grid. Rendering stays viewer-local by design (display and bucketing are different concerns); countWeeks period sizing deliberately stays UTC (shadowed by the authoritative plan totalSessions).

ADR B10 — the initialAllocation guard. The dialog sends initialAllocation: true for fresh PENDING allocations (only when tentativeSlotCount === 0); the server then rejects with a typed 409 if any confirmed (non-tentative) slot already exists — checked under the distributed locks and re-checked inside the write transaction on all three modes (manual/auto/requested). Reschedule and re-allocation flows omit the flag, preserving their replace semantics.

Idempotency + 409 UX. Every client allocate path now sends an Idempotency-Key (a retry of the identical payload reuses the key so the server replays the original batch; any payload change mints a fresh UUID). On 409 the dialog shows an "already allocated in another tab or by a teammate" toast, closes, and refreshes the request list; window focus also refetches so a stale tab self-heals.

Requested-path parity. Client preAllocate is renamed allocateRequestedSlots (matching the server's requested mode) and now passes totalSessions and subtracts pastConfirmedSlotCount, identical to manual/auto.

Cleanups. Selection validators extracted from the 2,226-line hook into pure, unit-tested slotSelectionValidation.ts (also the seed for the #997 Phase-3 server migration); all user-facing strings centralized in allocationMessages.ts; toast queue replaces the clobber-prone single slot; dead _-prefixed validators, the duplicated local-week grouper, and four copy-pasted fetch wrappers deleted; the 4-weeks/month fallback replaced by a disabled "plan configuration incomplete" row with a Sentry event; Sentry captureException added to all client engine catch paths (subsystem: client, feature: slot-allocation, no PII).

Testing

  • 628 jest tests green (__tests__/booking-algorithm + __tests__/schedule), including six new suites: slot-boundary-bucketing (IST/UTC/DST boundary keys, client/server parity), required-slots-periods (28/29/30/31-day months, leap Feb 2028, 4/5/6-Sunday-week periods, callsPerWeek 1–7 × session durations 0.5–2h, totalSessions override, missing-period throw), mode-parity (auto output passes manual validation; requested honors totals/past counts; consecutive atoms at day boundaries), toast-queue, idempotency-key, and initial-allocation-guard (409 with the flag, replace semantics preserved without it, tentative holds don't trip it).
  • Full tsc --noEmit clean; eslint clean on all new files.
  • Interactive dev-server round (IST boundary clicks, double-submit replay, two-tab 409, cross-mode race, curl idempotency) queued before merge.

Out of scope / follow-ups

Part of #997.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added scheduling-timezone–aware day/week bucketing for consistent slot validation.
    • Added idempotent requested allocation with idempotency keys and stronger multi-tab conflict handling.
    • Added “initial allocation” support to booking/rescheduling flows, including improved requested-slot handling for partial reschedules.
    • Standardized allocation toasts and queued notifications; show a clear “plan config incomplete” state when required data is missing.
  • Bug Fixes
    • Resolved calendar/server mismatches near timezone boundaries, week limits, consecutive sessions, and required-slot math.
  • Documentation
    • Updated ADRs and troubleshooting/changelog notes for scheduling-timezone and initial-allocation 409 behavior.
  • Tests
    • Expanded and updated Jest coverage for allocation parity, idempotency, timezone bucketing, guards, and toast behavior.

… allocation, requested-path parity

Unify every daily/weekly limit bucket on the event's schedulingTimezone
(default Asia/Kolkata) via shared SlotCalculationService keys across the
client guards, the client auto-allocator, and the server validators
(ADR B9); add the initialAllocation multi-tab 409 guard (ADR B10); send
Idempotency-Key from all client allocate paths with 409 recovery UX;
fix the requested-slots path ignoring plan totals and in-progress
reschedule reductions; remove the callsPerWeek*4*months fallback; queue
toasts through a single message catalog; extract the selection
validators to a pure, unit-tested module.

Part of the Request Calendar work tracked in #997.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@netlify

netlify Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploy Preview for familiarise ready!

Name Link
🔨 Latest commit a17c2ba
🔍 Latest deploy log https://app.netlify.com/projects/familiarise/deploys/6a5a224deb63a60008e1a77b
😎 Deploy Preview https://deploy-preview-998--familiarise.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 77 (🟢 up 16 from production)
Accessibility: 99 (🟢 up 3 from production)
Best Practices: 92 (🟢 up 9 from production)
SEO: 100 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@teetangh, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8476c2dd-4f5a-4a39-802f-cc8f7f252449

📥 Commits

Reviewing files that changed from the base of the PR and between be37504 and a17c2ba.

📒 Files selected for processing (5)
  • __tests__/booking-algorithm/mode-parity.test.ts
  • app/dashboard/consultant/[consultantId]/(features)/shared/hooks/useSlotAllocation.ts
  • app/dashboard/consultant/[consultantId]/(features)/shared/utils/allocationAlgorithms.ts
  • app/dashboard/consultant/[consultantId]/(features)/shared/utils/slotSelectionValidation.ts
  • utils/slotAllocation/SlotAllocationService.ts
📝 Walkthrough

Walkthrough

Booking allocation now uses explicit scheduling-timezone day/week keys across client and server validation, adds idempotent allocation attempts and an initialAllocation conflict guard, centralizes allocation feedback, renames requested-slot allocation, and expands automated coverage and documentation.

Changes

Booking allocation correctness

Layer / File(s) Summary
Scheduling-timezone bucketing contracts
utils/slotAllocation/SlotCalculationService.ts, utils/slotAllocation/SlotValidationService.ts, utils/subscriptionValidation.ts
Day and week grouping now uses explicit scheduling-timezone keys across allocation validation and subscription capacity calculations.
Shared client validation and calendar behavior
app/dashboard/consultant/.../shared/utils/slotSelectionValidation.ts, .../calendarUtils.ts, .../UnifiedCalendar.tsx
Client slot constraints, progress calculations, weekly limits, and calendar guards use shared timezone-aware validation and message helpers.
Allocation routing, idempotency, and conflict guards
.../allocationService.ts, .../allocationAlgorithms.ts, utils/slotAllocation/SlotAllocationService.ts, app/api/bookings/*/allocate/route.ts
Allocation requests carry idempotency and initialAllocation data; guarded allocations reject existing confirmed slots with HTTP 409.
Client allocation workflow and feedback
.../useSlotAllocation.ts, .../RequestSlotAllocationTab.tsx, .../allocationMessages.ts
Allocation attempts reuse deterministic keys, toast messages are queued and standardized, conflicts are surfaced, and incomplete plan data disables allocation actions.
Allocation and validation coverage
__tests__/booking-algorithm/*
Tests cover timezone boundaries, required-slot math, mode parity, requested allocation, idempotency, conflict guards, and toast queue behavior.
Booking documentation updates
docs/booking/*
ADRs, validation rules, troubleshooting guidance, changelog entries, and test references describe the updated allocation behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers: shubham79

Poem

A rabbit sorts slots by moonlit key,
With Sunday weeks where they should be.
Idempotent hops repeat no more,
Conflicts guard the allocation door.
Toasts queue neatly, soft and bright—
“Timings saved!” says Bunny tonight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 69.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: timezone-based bucketing, guarded/idempotent allocation, and requested-path parity.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/booking-algorithm-calendar

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.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive correctness sweep for the booking calendar system, aligning client-side guards, the auto-allocator, and server-side validators to use the event's scheduling timezone (ADR B9) and implementing an initialAllocation multi-tab guard (ADR B10) with idempotency key wiring. Review feedback suggests defensive improvements to prevent negative slot counts, optimizing map iterations in the newly extracted validation utility, and verifying that the Prisma namespace is properly imported to avoid compilation errors.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread utils/slotAllocation/SlotAllocationService.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 13

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@__tests__/booking-algorithm/initial-allocation-guard.test.ts`:
- Around line 70-85: Extend the initial-allocation guard tests for both manual
and auto modes so slotOfAppointment.count returns zero before allocation but a
transaction-scoped count reports confirmed slots. Assert the allocation returns
the typed 409 response and that no conflicting write proceeds, covering the
transaction race-window re-check rather than only the preliminary count path.

In `@__tests__/booking-algorithm/mode-parity.test.ts`:
- Around line 78-128: Update the mode-parity test cases around
AllocationAlgorithms.autoAllocate and the additional case to define one explicit
non-default schedulingTimezone, then pass it consistently through the allocation
options, validationOptions, validateEventSlots, validateSlotDistribution, and
groupSlotsByDay calls. Preserve the existing assertions while ensuring all modes
evaluate slots using the same explicit event timezone rather than the fallback.
- Around line 212-225: Extend the test to validate the authoritative
totalSessions behavior across requested, auto, and shared allocation modes,
including a five-week-bucket case where the four-session plan must not require
five calls. Update getSlotLimits and each relevant allocation path to forward
the plan’s maxTotalCalls into requiredSlots calculation, preserving consistent
plan-total parity across all modes.

In
`@app/dashboard/consultant/`[consultantId]/(features)/requests/RequestSlotAllocationTab.tsx:
- Around line 373-377: Update the attempt-key handling around the allocation
request and the related usage at the second location to use a ref instead of
React state, matching the shared allocation hook. Generate and store the key
synchronously in the ref before issuing the request, and reuse that ref value
across rapid successive clicks so both requests share the same idempotency key.

In
`@app/dashboard/consultant/`[consultantId]/(features)/shared/hooks/useSlotAllocation.ts:
- Around line 593-615: Convert slotLimits.maxSlots to the equivalent session
count before using it in the call-limit logic. Apply this consistently to
maxTotalCalls and the related declarations/usages around sessionAdded, including
the additional locations noted at 707-709, 787-798, and 810, so all comparisons
use sessions rather than 30-minute slots.

In
`@app/dashboard/consultant/`[consultantId]/(features)/shared/utils/allocationService.ts:
- Around line 27-32: Update the AllocationResponse interface to type data as an
array of allocation appointment DTOs matching result.appointments returned by
all allocation routes, replacing the date-keyed RawAvailabilityApiSlot record.
Reuse an existing appointment DTO type if available; otherwise define the
appropriate DTO near AllocationResponse, and update affected imports or
consumers to preserve the corrected contract.

In
`@app/dashboard/consultant/`[consultantId]/(features)/shared/utils/calendarUtils.ts:
- Around line 590-593: Update calendarUtils.ts lines 590-593 so day-based
validation and progress helpers accept and use the event scheduling timezone
when calling SlotCalculationService.dayKey. In UnifiedCalendar.tsx lines
193-218, pass that timezone into the final completed-call validation. In
UnifiedCalendar.tsx lines 580-608, replace browser-local toDateString() boundary
checks with scheduling-timezone dayKey comparisons.

In
`@app/dashboard/consultant/`[consultantId]/(features)/shared/utils/slotSelectionValidation.ts:
- Around line 586-819: Refactor validateEventSlots into a lightweight dispatcher
that preserves the shared empty-selection and common setup behavior, then
delegates webinar, class, subscription, and consultation validation to separate
focused validator functions. Move each event-specific branch and its related
weekly checks into the appropriate validator, preserving all existing
ValidationResult fields, errors, warnings, and validation behavior while
reducing validateEventSlots cognitive complexity below the allowed threshold.
- Around line 280-307: Update countSessionsForDay so each consecutive run is
evaluated independently and any run shorter than slotsPerSession is reported as
incomplete, rather than allowing leftovers to be combined across disconnected
runs. Propagate this incomplete-run state through the allocation-readiness
checks at the related subscription/class validation paths, and reject selections
containing any incomplete run even when total slot counts are divisible by
slotsPerSession.

In `@utils/slotAllocation/SlotAllocationService.ts`:
- Around line 265-296: Make assertNoConfirmedSlots atomic by acquiring an
event-scoped lock shared across auto, manual, and requested allocation modes
before counting confirmed slots, and hold that lock through the subsequent write
transaction. Apply the same locking/claiming behavior at every listed
initial-allocation call site so concurrent transactions cannot both pass the
zero-slot check and commit competing allocations.
- Around line 108-113: Update the requested-mode path in the switch and
useRequestedSlots to propagate request.idempotencyKey. Before the confirmed-slot
guard, detect and return the prior successful result for a matching key; within
the transaction, stamp the first existing appointment with the key when
processing the initial request so retries replay success instead of returning
409.

In `@utils/slotAllocation/SlotCalculationService.ts`:
- Around line 145-198: The recurring totals calculation still uses UTC week
boundaries while limit buckets use the scheduling timezone. In
utils/slotAllocation/SlotCalculationService.ts:145-198, update countWeeks and
its recurring-calculation callers to accept and use the scheduling timezone via
the existing week infrastructure. In utils/subscriptionValidation.ts:80-91, pass
schedulingTimezone into that calculation so maxTotalCalls aligns with generated
weekly buckets.

In `@utils/slotAllocation/SlotValidationService.ts`:
- Around line 1061-1065: Update the session-building loop in
SlotValidationService to create a chronological copy of slots before iterating
in slotsPerSession-sized steps. Sort the copy by slot start time while
preserving the original slots array, then use that sorted collection for dayKey
and subsequent session calculations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 416c73c2-2657-4567-b9f1-62a92fd54d75

📥 Commits

Reviewing files that changed from the base of the PR and between 7362fe3 and 176bee3.

📒 Files selected for processing (33)
  • __tests__/booking-algorithm/allocationAlgorithms.test.ts
  • __tests__/booking-algorithm/idempotency-key.test.ts
  • __tests__/booking-algorithm/initial-allocation-guard.test.ts
  • __tests__/booking-algorithm/mode-parity.test.ts
  • __tests__/booking-algorithm/required-slots-periods.test.ts
  • __tests__/booking-algorithm/slot-boundary-bucketing.test.ts
  • __tests__/booking-algorithm/slotCalculationService.test.ts
  • __tests__/booking-algorithm/subscriptionValidation.test.ts
  • __tests__/booking-algorithm/toast-queue.test.ts
  • app/api/bookings/classes/[classId]/allocate/route.ts
  • app/api/bookings/consultations/[consultationId]/allocate/route.ts
  • app/api/bookings/subscriptions/[subscriptionId]/allocate/route.ts
  • app/api/bookings/webinars/[webinarId]/allocate/route.ts
  • app/dashboard/consultant/[consultantId]/(features)/requests/RequestSlotAllocationTab.tsx
  • app/dashboard/consultant/[consultantId]/(features)/requests/types.ts
  • app/dashboard/consultant/[consultantId]/(features)/shared/components/UnifiedCalendar.tsx
  • app/dashboard/consultant/[consultantId]/(features)/shared/hooks/useSlotAllocation.ts
  • app/dashboard/consultant/[consultantId]/(features)/shared/utils/allocationAlgorithms.ts
  • app/dashboard/consultant/[consultantId]/(features)/shared/utils/allocationMessages.ts
  • app/dashboard/consultant/[consultantId]/(features)/shared/utils/allocationService.ts
  • app/dashboard/consultant/[consultantId]/(features)/shared/utils/calendarUtils.ts
  • app/dashboard/consultant/[consultantId]/(features)/shared/utils/slotSelectionValidation.ts
  • docs/booking/00-architecture-decisions.md
  • docs/booking/02-event-types-and-validation.md
  • docs/booking/03-slot-math-and-calculations.md
  • docs/booking/05-troubleshooting-and-changelog.md
  • docs/booking/14-local-development-and-testing.md
  • schemas/slotAllocation/validationSchemas.ts
  • utils/slotAllocation/SlotAllocationService.ts
  • utils/slotAllocation/SlotCalculationService.ts
  • utils/slotAllocation/SlotValidationService.ts
  • utils/slotAllocation/types.ts
  • utils/subscriptionValidation.ts

Comment thread __tests__/booking-algorithm/initial-allocation-guard.test.ts
Comment thread __tests__/booking-algorithm/mode-parity.test.ts
Comment thread __tests__/booking-algorithm/mode-parity.test.ts
Comment thread utils/slotAllocation/SlotAllocationService.ts
Comment thread utils/slotAllocation/SlotAllocationService.ts
Comment thread utils/slotAllocation/SlotCalculationService.ts
Comment thread utils/slotAllocation/SlotValidationService.ts Outdated
teetangh and others added 2 commits July 17, 2026 17:51
…city, requested-mode idempotency

CI: replace machine-local getDay() assertions with scheduling-timezone /
UTC-explicit checks (suite now green under UTC, IST, and New York).
Sonar: split validateEventSlots into per-event validators (complexity
49→dispatcher), explicit sort comparator, crypto-only key fallback,
optional chains, readonly formatter cache, drop redundant jumps and an
always-truthy guard. CodeRabbit: advisory xact lock makes the
initialAllocation guard atomic across modes for lock-less group events;
requested mode now replays and stamps Idempotency-Keys; the requests
tab attempt key moves to a ref (double-click safe); allocation response
typed as appointment DTOs; getSlotLimits honors the plan total;
manual allocate rejects incomplete per-day runs; per-day cap sorts its
input; scheduling timezone threaded into auto-expand and progress
helpers. Adds race-window and non-default-timezone tests.

Part of #998 review triage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ction narrowing

lib.dom types every Crypto member as present, so an `in` check collapses
the fallback branch to never; detect via optional access instead. Mock
allocation data as an array to match the new DTO type.

Part of #998 review triage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/dashboard/consultant/[consultantId]/(features)/shared/utils/slotSelectionValidation.ts (1)

184-202: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reduce minSlots by past confirmed slots too.

maxSlots represents remaining calls, but minSlots remains the full-plan slot count. An in-progress subscription can therefore require more slots than its maximum permits.

Proposed fix
       const pastSessions = Math.floor(
         (options.pastConfirmedSlotCount || 0) / subscriptionSessionSlots,
       );
+      const remainingRequiredSlots = Math.max(
+        0,
+        requiredSlots - (options.pastConfirmedSlotCount || 0),
+      );
       return {
-        minSlots: requiredSlots,
+        minSlots: remainingRequiredSlots,
         maxSlots: Math.max(0, rawMaxCalls - pastSessions),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/dashboard/consultant/`[consultantId]/(features)/shared/utils/slotSelectionValidation.ts
around lines 184 - 202, Update the subscription branch in the slot-selection
validation logic so minSlots is reduced by options.pastConfirmedSlotCount,
matching the remaining-session calculation used for maxSlots. Preserve the
existing non-negative maxSlots behavior and ensure minSlots reflects only the
remaining required slots for in-progress subscriptions.
♻️ Duplicate comments (1)
utils/slotAllocation/SlotCalculationService.ts (1)

145-198: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Recurring totals still use UTC week counts while allocation limits use scheduling-timezone weeks.

  • utils/slotAllocation/SlotCalculationService.ts#L145-L198: extend the canonical timezone week infrastructure to cover countWeeks.
  • app/dashboard/consultant/[consultantId]/(features)/shared/components/UnifiedCalendar.tsx#L503-L510: derive the fallback total with the event scheduling timezone.
  • app/dashboard/consultant/[consultantId]/(features)/shared/utils/allocationAlgorithms.ts#L703-L714: calculate totalWeeks using the same timezone as slotsByWeek.
  • utils/slotAllocation/SlotAllocationService.ts#L1542-L1547: use timezone-aware week counting for the server iteration bound.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@utils/slotAllocation/SlotCalculationService.ts` around lines 145 - 198,
Recurring totals count weeks in UTC while allocation limits use the scheduling
timezone. In utils/slotAllocation/SlotCalculationService.ts lines 145-198, add
timezone-aware week-counting infrastructure alongside weekKey and
startOfWeekSundayInTz, including a countWeeks API. Update UnifiedCalendar.tsx
lines 503-510 to derive fallback totals with the event scheduling timezone,
allocationAlgorithms.ts lines 703-714 to calculate totalWeeks using the same
timezone as slotsByWeek, and SlotAllocationService.ts lines 1542-1547 to use
timezone-aware week counting for the server iteration bound.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@app/dashboard/consultant/`[consultantId]/(features)/shared/hooks/useSlotAllocation.ts:
- Around line 302-308: Update generateIdempotencyKey to replace the "randomUUID"
in crypto check with a typeof crypto.randomUUID === "function" check inside the
existing crypto availability guard, preserving the fallback getRandomValues
branch.

In
`@app/dashboard/consultant/`[consultantId]/(features)/shared/utils/slotSelectionValidation.ts:
- Around line 664-670: The aggregate validation incorrectly treats disconnected
slots on the same day as complete sessions. In slotSelectionValidation.ts lines
664-670, derive hasInProgress from the leftover slots in each consecutive run
rather than the day-total modulo; in allocationAlgorithms.ts lines 195-210,
validate every session-sized run for exact slot adjacency instead of validating
only the aggregate day count.

In `@utils/slotAllocation/SlotAllocationService.ts`:
- Around line 1081-1099: After acquiring the advisory event lock inside the
prisma.$transaction callback, call findIdempotentAllocation using the
transaction client before evaluating initialAllocation or
assertNoConfirmedSlotsInTx. Return the transaction-local replay result when
present, while preserving the existing guard for genuinely new allocations and
the pre-transaction replay check.

---

Outside diff comments:
In
`@app/dashboard/consultant/`[consultantId]/(features)/shared/utils/slotSelectionValidation.ts:
- Around line 184-202: Update the subscription branch in the slot-selection
validation logic so minSlots is reduced by options.pastConfirmedSlotCount,
matching the remaining-session calculation used for maxSlots. Preserve the
existing non-negative maxSlots behavior and ensure minSlots reflects only the
remaining required slots for in-progress subscriptions.

---

Duplicate comments:
In `@utils/slotAllocation/SlotCalculationService.ts`:
- Around line 145-198: Recurring totals count weeks in UTC while allocation
limits use the scheduling timezone. In
utils/slotAllocation/SlotCalculationService.ts lines 145-198, add timezone-aware
week-counting infrastructure alongside weekKey and startOfWeekSundayInTz,
including a countWeeks API. Update UnifiedCalendar.tsx lines 503-510 to derive
fallback totals with the event scheduling timezone, allocationAlgorithms.ts
lines 703-714 to calculate totalWeeks using the same timezone as slotsByWeek,
and SlotAllocationService.ts lines 1542-1547 to use timezone-aware week counting
for the server iteration bound.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 80bd2e8b-22d8-41dc-b49e-908092ebdd9d

📥 Commits

Reviewing files that changed from the base of the PR and between 176bee3 and be37504.

📒 Files selected for processing (14)
  • __tests__/booking-algorithm/calendarUtils.test.ts
  • __tests__/booking-algorithm/initial-allocation-guard.test.ts
  • __tests__/booking-algorithm/mode-parity.test.ts
  • __tests__/booking-algorithm/subscriptionValidation.test.ts
  • app/dashboard/consultant/[consultantId]/(features)/requests/RequestSlotAllocationTab.tsx
  • app/dashboard/consultant/[consultantId]/(features)/shared/components/UnifiedCalendar.tsx
  • app/dashboard/consultant/[consultantId]/(features)/shared/hooks/useSlotAllocation.ts
  • app/dashboard/consultant/[consultantId]/(features)/shared/utils/allocationAlgorithms.ts
  • app/dashboard/consultant/[consultantId]/(features)/shared/utils/allocationService.ts
  • app/dashboard/consultant/[consultantId]/(features)/shared/utils/calendarUtils.ts
  • app/dashboard/consultant/[consultantId]/(features)/shared/utils/slotSelectionValidation.ts
  • utils/slotAllocation/SlotAllocationService.ts
  • utils/slotAllocation/SlotCalculationService.ts
  • utils/slotAllocation/SlotValidationService.ts

Comment thread utils/slotAllocation/SlotAllocationService.ts Outdated
…-aware session completeness

The advisory-locked guard now re-checks idempotent replay after acquiring
the event lock (all three modes), so a same-key double submit that lost
the pre-transaction race replays the winner's batch instead of 409ing.
Day completeness checks decompose each day into complete CONSECUTIVE
sessions via countSessionsForDay — a bare length-modulo let disconnected
fragments masquerade as complete sessions.

Part of #998 review triage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@teetangh teetangh self-assigned this Jul 17, 2026
@sonarqubecloud

Copy link
Copy Markdown

@teetangh
teetangh merged commit e4cb92d into dev Jul 17, 2026
8 checks passed
@teetangh
teetangh deleted the fix/booking-algorithm-calendar branch July 17, 2026 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant