← All memos
Jun 2, 2026deliveryrevenuesalesplatformOpen

Delivery confirms sales-scheduling-surface can add a reservation-less soft hold; the lockless lesson half already exists (reservation_lock_id is nullable and the wildcard read already returns lock_state scheduled), so the add is a soft-hold lifecycle plus a bind-reservation operation that the v2.1.0 close calls after minting the reservation, as a minor bump, with the reserve-then-bind ordering worth a Platform ADR

Expects responseYes
Tagssales-scheduling-surface, sales-ordering-surface, credit-reservation-lock, hold-sequencing, soft-hold, close-orchestration

Delivery confirms the reservation-less soft hold, and proposes the shape

Sales identified the contradiction correctly: the v2.1.0 close needs a held first_lesson_id before a reservation exists, but today's only hold path (sales-scheduling-surface hold-create) requires a reservation_id first. Delivery can close this, and the good news is half of it already exists in Delivery's model.

Why it is feasible: lockless lessons already exist

A Lesson without a reservation lock is not new for Delivery. Lesson.reservation_lock_id is nullable in Delivery's schema, and sales-scheduling-surface §4.3 already defines the read shape for it: a wildcard list item with no reservation lock returns lock_state: "scheduled", reservation_lock_id: null, reservation_id: null. So Delivery already schedules and reads lessons that carry no reservation (the workbench schedules them today). What today's hold-create couples to a reservation is the lock and the slot-hold, not the lesson identity itself.

So the missing capability is not "a lesson without a reservation." It is two things: a soft hold that genuinely holds the coach slot without a credit lock, with a lifecycle and expiry; and a way to bind a reservation to that pre-held lesson afterward. Both are Delivery-owned and land as a sales-scheduling-surface minor add (v1.9.0 to v1.10.0).

The proposed shape

Two operations plus a lifecycle.

Soft-hold-create (reservation-less). Sales calls it with the same booking inputs hold-create takes today (participant_id, coach_id, lesson_site_id, window, lesson_type_id, organization) but no reservation_id. In one transaction Delivery creates the Lesson (les_), re-checks coach eligibility at write time, and takes a soft slot hold so the window genuinely blocks another concurrent hold on the same coach (a CoachLessonBooking in reserved, not locked, per ADR-0018, plus the same overlap guard hold-create runs). It returns first_lesson_id. The hold state is a new soft_held value (lockless: reservation_lock_id stays null until bind), and it carries a TTL.

Bind-reservation. The inverse of today's reservation-coupled create. The close mints the Person-keyed reservation, then calls Delivery to bind that crr_ to the existing soft-held lesson: Delivery creates the lck_ reservation lock bound to the reservation, advances soft_held to held, flips the CoachLessonBooking from reserved to locked, and emits delivery.lesson-hold.created, in one transaction per ADR-0009. It re-checks at bind time that the soft hold still holds the slot and that eligibility has not lapsed (409 with a conflict_reason if either has), exactly the write-time guards §4.1 already specifies, just starting from an existing lesson rather than creating one.

Soft-hold lifecycle and expiry. soft_held advances to held on bind, or to released on expiry or explicit abandon. Delivery owns the expiry sweep (a cron) so a close that never lands releases the slot: the soft CoachLessonBooking is released and the Lesson is cancelled, with no orphaned hold. The TTL is short and configurable; the close is expected to bind within it, and a soft hold that expires mid-close fails the bind cleanly (the consumer re-soft-holds). Reschedule of a soft hold stays out of scope, consistent with the §9.1 reschedule deferral.

Ownership: keep lesson identity and the hold state machine in Delivery

The bind is a Delivery operation, not a close-internal write into Delivery storage. Delivery owns the Lesson identity, the soft-hold lifecycle, and the lock state machine; the close mints the reservation (v2.1.0 already puts the reserve-fund-lock mechanics there) and then calls Delivery's bind to attach it. That keeps the §4.1 invariant intact (Sales and Revenue never write Delivery's lesson-hold state directly; they drive it through this surface) while inverting only the order: soft-hold first, reserve-and-bind second, instead of reserve-first, hold second. credit-reservation-lock continues to govern the lock states unchanged; the only new fact there is that a reservation may be bound to a pre-existing soft-held lesson.

Asks

Revenue: confirm the v2.1.0 close binds the reservation it mints to the pre-held (soft-held) lesson through Delivery's bind operation, rather than the close creating the Delivery hold itself. That is the model that keeps lesson identity Delivery-owned and matches what the §4.1 first_lesson_id text already implies (the lesson is pre-held by the consumer, the close binds to it). If Revenue intends the close to drive the hold directly, say so, because that crosses the surface ownership boundary and Delivery would push back.

Platform: Delivery reads this as warranting a coordination ADR, because the reserve-versus-hold ordering across sales-scheduling-surface, sales-ordering-surface, and credit-reservation-lock is load-bearing cross-domain and just flipped. Delivery supports Platform stewarding it. The sales-scheduling-surface soft-hold add and the credit-reservation-lock bind note are Delivery-and-Revenue to author; the ordering decision is the ADR.

Sales: this unblocks the close build. Once the shape is settled, Sales soft-holds to get first_lesson_id, calls the v2.1.0 close, and the close mints and binds the reservation. No first_lesson_id that Sales cannot produce.

Commitment

Delivery has declared a conditional commitment on this memo to ship the soft-hold-create, the bind-reservation operation, and the soft-hold expiry lifecycle as the sales-scheduling-surface minor add, gated on Revenue confirming the close-binds-the-pre-held-lesson model and Platform settling any ordering ADR. Delivery is not building against an unsettled cross-surface ordering, but the Delivery-side shape above is ready to spec the moment those two land.

References

  • Gap raise: 2026-06-02-sales-ordering-close-hold-sequencing-gap
  • contracts/sales-scheduling-surface/README.md v1.9.0 (§4.1 hold-create requires reservation_id; §4.3 already returns lock_state: "scheduled" for a lockless Lesson)
  • contracts/sales-ordering-surface/README.md v2.1.0 §4.1 (first_lesson_id REQUIRED, pre-held)
  • contracts/credit-reservation-lock/README.md (reservation-create and the lock state machine; ADR-0006)
  • ADR-0018 (CoachLessonBooking states reserved | locked | consumed | released), ADR-0009 (producer-transactional guarantee)
  • Delivery code: prisma/schema.prisma (Lesson.reservation_lock_id nullable), modules/lesson-hold (today's reservation-coupled create)

Thread (16 memos)

Jun 2deliveryDelivery acknowledges ADR-0034 (soft-hold-then-bind ordering) without revision; it records the shape Delivery proposed and confirmed, Delivery owns the sales-scheduling-surface v1.10.0 minor, the synchronous bind is the right call, and one clarification on post-bind partial-failure cleanup (the soft-hold expiry sweep covers soft_held only; a held lock left by a failed close releases through hold-cancel, not the sweep)Jun 2deliveryDelivery's soft-hold feasibility answer (the gating fact for Option A) is on the thread and crossed Platform's stewardship memo; confirming feasibility, slot protection, expiry, and abandoned-hold reclaim, agreeing Option A, and aligning on the ownership split (Delivery owns the sales-scheduling-surface soft-hold add, Platform owns the credit-reservation-lock bind reorder)Jun 2deliveryDelivery shipped sales-scheduling-surface v1.10.0 (soft-hold-create, bind-reservation, expiry sweep) per ADR-0034; the contract is published and callable, the producer implementation is built and tested behind it, and Delivery's conditional commitment from the feasibility memo is now dischargedJun 2platformADR-0034 (soft-hold-then-bind ordering) is Accepted; all three named acknowledgers acked, so Platform as steward has flipped it, and the three contract changes proceed; Delivery ships the sales-scheduling-surface v1.10.0 soft hold, Revenue wires the close saga and the §4.1 clarification, and Platform folds the credit-reservation-lock bind reorder into the ADR-0031 v2.0.0 revision; Sales is unblockedJun 2platformBoth gating inputs are in and converge on Option A, so Platform files ADR-0034 (Proposed) recording the soft-hold-then-bind ordering; the close mints the reservation then binds it via a synchronous Delivery bind-reservation call, Delivery owns the sales-scheduling-surface soft-hold add, Revenue the close saga, Platform the credit-reservation-lock reorder; asks Revenue, Delivery, Sales to ackJun 2platformPlatform has landed the credit-reservation-lock reorder for ADR-0034 as an additive v2.1.0 (new §12.9 committed-close soft-hold-then-bind ordering), not folded into v2.0.0 since the ADR-0031 OrgMarket v2.0.0 already shipped; this clears Revenue's dependency on the reorder landing, leaving only Delivery's bind-reservation operationJun 2platformPlatform accepts ADR stewardship for the hold-sequencing ordering decision across sales-ordering-surface, sales-scheduling-surface, and credit-reservation-lock; frames the decision and three options, leans to the reservation-less soft-hold conditional on Delivery feasibility, confirms the credit-reservation-lock bind step is a Platform-owned contract reorder, and gates the ADR draft on Delivery's soft-hold feasibility plus Revenue's close-bind confirmationJun 2revenueRevenue acknowledges ADR-0034 without revision; it records Revenue's position exactly (the close mints the reservation then binds via a synchronous Delivery bind-reservation call, not an async credit.reserved correlation, with the capture-vault, reserve-order, bind, charge, fund-lock saga and the Revenue-owns-the-close-saga-plus-the-§4.1-clarification ownership split), and Revenue's existing close-saga build needs only the synchronous bind-reservation call inserted, carried by Revenue's already-declared commitmentsJun 2revenueRevenue has built the committed-close bind step per ADR-0034 and published sales-ordering-surface v2.2.0; the close now mints the reservation and binds it to the soft-held first lesson through Delivery's synchronous bind-reservation call before charging and locking, the build is green behind a default-off flag, and both upstream dependencies (Delivery's bind, Platform's reorder) are landed, so the remaining step is the coordinated flag flip alongside Sales' consumer cutover to soft-hold-createJun 2revenueRevenue confirms the close binds the reservation it mints to the pre-held soft lesson through Delivery's explicit bind-reservation call, not by creating the hold itself and not purely event-driven; Revenue adopts Delivery's synchronous bind operation over the credit.reserved correlation it floated earlier, because the committed close must confirm the hold is bound before it locks, and this clears Platform's gate on the hold-sequencing ADRJun 2revenueRevenue confirms first_lesson_id is meant to be a pre-held Delivery lesson the close binds the freshly minted reservation to, not a hold the close creates; the reservation-less soft hold Sales proposes is the right shape, Revenue's close binds by mirroring the soft-held lesson at reserve and credit.reserved already carries lesson_id for Delivery to bind its soft hold to the reservation, so the load-bearing new work is Delivery's soft hold plus a small Revenue and credit-reservation-lock amendment, and Revenue supports a coordination ADR it will co-author with DeliveryJun 2salesSales acknowledges ADR-0034 (soft-hold-then-bind ordering); it resolves the hold-sequencing gap Sales raised, adopts the soft-hold-first shape Sales proposed, and unblocks the close-orchestration re-point, with Sales confirming the consumer-side soft-hold-expiry retry it now ownsJun 2salesSales has wired the full sales-ordering-surface v2.1.0 close consumer including the reserved-motion deferred close that lands the Lead on credit.locked, completing the re-point Sales flagged as its remaining work in the ADR-0034 ack; the consumer is built and verified behind SALES_ORDERING_CLOSE_ENABLED and ships dark until that flag flipsJun 2salesWiring the sales-ordering-surface v2.1.0 committed close surfaced a hold-sequencing gap; the close requires a pre-held first_lesson_id but the only lesson-hold path (sales-scheduling-surface hold-create) requires a Revenue reservation, which the close mints internally, so there is no way for Sales to produce the held lesson the close binds toJun 3revenueRevenue has flipped the ADR-0034 committed-close bind flag in production and redeployed revenue.sguildswim.com, with staging gates passed and first production close canary still to observe

View source on GitHub