Summary
The reschedule-proposal system shipped one-sided. RescheduleRequestStatus defines ACCEPTED, DECLINED, and COUNTERED, and the transition map allows them (lib/booking/transitions.ts:186-200) — but no user-facing endpoint or surface lets the counterparty answer a proposal. This was never built and then lost; the git history shows #1064 shipped withdraw + auto-confirm and stopped there.
Evidence
transitionRescheduleRequest has exactly three production callers: the allocator writes ACCEPTED as a side-effect of the consultant allocating against the request (utils/slotAllocation/SlotAllocationService.ts:636-639), auto-confirm writes AUTO_ACCEPTED (lib/booking/reschedule-auto-confirm.ts:108-115), and withdraw writes WITHDRAWN. The only DECLINED writer in the codebase is the cancel route closing proposals as a side-effect of booking cancellation — and it does so via a raw updateMany that bypasses the CAS helper entirely (app/api/appointments/[appointmentId]/cancel/route.ts:367-377). COUNTERED is written by nothing except seed data.
The consequences compound on both sides of the journey:
- After a consultant proposes times, the toast says "The consultee has been asked to accept the new time" (
useConsultantEventActions.ts:106-111) — but the consultee's queries never include rescheduleRequests (the sole include is the consultant-inbox select at lib/booking/list-selects.ts:58), so the consultee sees only "Awaiting schedule confirmation" and can never respond. Both parties wait for an answer that cannot arrive; the proposal can only expire (≤72h) or be withdrawn.
- The withdraw endpoint itself (
POST /api/appointments/[appointmentId]/reschedule/withdraw) has zero UI callers — a released booking cannot be retracted from any surface.
- In the consultant inbox, a displayed consultee proposal has no accept affordance: "Use Requested Times" is suppressed whenever released slots exist (
components/dashboard/shared/requests/RequestSlotAllocationTab.tsx:1071), so the consultant must manually re-find the proposed time on the grid.
Fix direction
Build the response loop: accept/decline (and counter) endpoints beside the existing withdraw route, CAS transitions for all three statuses (including fixing the cancel route's raw updateMany), a consultee-facing proposal surface with accept/decline affordances, wiring for the orphaned withdraw UI, and the inbox accept path. Lands in the booking + maintenance productionization train (wave 2, lifecycle PR).
Summary
The reschedule-proposal system shipped one-sided.
RescheduleRequestStatusdefinesACCEPTED,DECLINED, andCOUNTERED, and the transition map allows them (lib/booking/transitions.ts:186-200) — but no user-facing endpoint or surface lets the counterparty answer a proposal. This was never built and then lost; the git history shows #1064 shipped withdraw + auto-confirm and stopped there.Evidence
transitionRescheduleRequesthas exactly three production callers: the allocator writesACCEPTEDas a side-effect of the consultant allocating against the request (utils/slotAllocation/SlotAllocationService.ts:636-639), auto-confirm writesAUTO_ACCEPTED(lib/booking/reschedule-auto-confirm.ts:108-115), and withdraw writesWITHDRAWN. The onlyDECLINEDwriter in the codebase is the cancel route closing proposals as a side-effect of booking cancellation — and it does so via a rawupdateManythat bypasses the CAS helper entirely (app/api/appointments/[appointmentId]/cancel/route.ts:367-377).COUNTEREDis written by nothing except seed data.The consequences compound on both sides of the journey:
useConsultantEventActions.ts:106-111) — but the consultee's queries never includerescheduleRequests(the sole include is the consultant-inbox select atlib/booking/list-selects.ts:58), so the consultee sees only "Awaiting schedule confirmation" and can never respond. Both parties wait for an answer that cannot arrive; the proposal can only expire (≤72h) or be withdrawn.POST /api/appointments/[appointmentId]/reschedule/withdraw) has zero UI callers — a released booking cannot be retracted from any surface.components/dashboard/shared/requests/RequestSlotAllocationTab.tsx:1071), so the consultant must manually re-find the proposed time on the grid.Fix direction
Build the response loop: accept/decline (and counter) endpoints beside the existing withdraw route, CAS transitions for all three statuses (including fixing the cancel route's raw
updateMany), a consultee-facing proposal surface with accept/decline affordances, wiring for the orphaned withdraw UI, and the inbox accept path. Lands in the booking + maintenance productionization train (wave 2, lifecycle PR).