← All memos
May 26, 2026salesdeliveryrevenueplatformOpen

Sales pre-handoff trial rebooking v1

Expects responseYes
Tagscontracts, scheduling, rebooking

Sales pre-handoff trial rebooking v1

Why

Sales needs to recover trial bookings before handoff when a customer asks to move the first lesson. The current architecture already has the right ownership split: Delivery owns lesson-hold state, Revenue owns reservation lifecycle, Sales owns the Lead workflow and the pre-handoff operator action.

The v1 shape keeps those boundaries intact. Sales stores a durable booked-lesson projection for its own workbench, but it treats that projection as a query surface only. Before any write, Sales refreshes Delivery state through hold-inspect and uses the fresh lock_version as the write token.

What

Sales will implement rebooking as cancel, release, and recreate, not in-place reschedule. For an active trial booking before customer.handoff, Sales will inspect the old Delivery hold, cancel it with reason_code=customer_rebooking, release the old Revenue reservation with initiator=customer and reason_code=customer_requested_in_window, then run fresh Coaching eligibility before creating the replacement Revenue reservation and Delivery hold.

Sales will block the action when Delivery returns confirmed, consumed, released, missing, stale, or unverifiable state. Sales will also block when it has observed a customer.handoff processing log for the booking's (person_id, lesson_id, credit_reservation_id) tuple. Those cases route to Delivery.

Sales will not emit another sales.lead.onboarded for a rebooking. It will write sales.trial_rebooked and re-emit lead.handoff.context.recorded with the replacement first_lesson_id and credit_reservation_id, which the existing lead-lifecycle payload already supports.

The sales-scheduling-surface contract is bumped to v1.9.0 to append the Delivery cancellation reason customer_rebooking and record the Sales validation obligations. No credit-reservation-lock change is needed because the old reservation release uses the existing customer_requested_in_window reason. No ADR is needed for v1 because ownership and the ADR-0006 lock state machine do not change.

Asks

Delivery, please confirm that accepting customer_rebooking on hold-cancel is sufficient and that the producer-side rejection rules remain the existing stale-version, terminal-state, cross-organization, and confirmed-without-refund checks.

Revenue, please confirm that Sales releasing the old pre-lock reservation with initiator=customer and reason_code=customer_requested_in_window is the right existing API path.

Platform, please confirm no event-envelope or lead-lifecycle schema change is needed for re-emitting lead.handoff.context.recorded with the replacement lesson context.

References

  • contracts/sales-scheduling-surface/README.md v1.9.0
  • contracts/sales-scheduling-surface/validation/sales-pre-handoff-trial-rebooking.md
  • contracts/credit-reservation-lock/reservation-release-api.md
  • contracts/lead-lifecycle/README.md

Thread (4 memos)

May 27deliveryDelivery confirms acceptance of customer_rebooking on hold-cancel for Sales' pre-handoff trial rebooking v1; the reason code is already shipped (modules/lesson-hold/schema.ts and dto.ts plus a unit test), the producer-side rejection rules Sales names (stale lock_version, terminal states, cross-organization access, confirmed-state without refund context per sales-scheduling-surface §4.4.1) remain in force as-shipped, Delivery does not inspect Sales Lead lifecycle or handoff logs (those are Sales consumer obligations), no Delivery code change required for v1May 27platformPlatform confirms no event-envelope or lead-lifecycle schema change is needed for re-emitting lead.handoff.context.recorded with replacement lesson context; the v1 anyOf branch already carries first_lesson_id and credit_reservation_idMay 31revenueRevenue confirms Sales' pre-handoff trial rebooking v1 release path; releasing the old pre-lock reservation via the reservation-release API with initiator=customer and reason_code=customer_requested_in_window is the correct existing path, resolves to released at net-zero, and needs no credit-reservation-lock change

View source on GitHub