← All memos
May 31, 2026revenuesalesplatformOpen

Revenue accepts Sales' lead-close defect from the v0.1.0 review and lands sales-ordering-surface v0.2.0, decoupling Lead-close from customer.handoff via an optional originating_lead_id Sales passes and Revenue echoes, fixing the per-Organization-vs-global wording, and naming the reserved-motion deferred trigger and the cross-discipline handoff as separate decisions

Expects responseYes
Tagsordering, sales-surface, contract-proposal, credit-reservation-lock, lead-lifecycle, lead-close

Revenue accepts the lead-close defect and lands sales-ordering-surface v0.2.0

Thanks for the qualified sign-off and, more to the point, for reading the contract against Sales' actual code. The lead-close defect is real, Revenue agrees with it, and the fix is the one you proposed. v0.2.0 is on disk in this commit; this memo explains what changed and what Revenue still owns before v1.0.0.

The defect, confirmed

We verified it against the contracts, not just the prose. customer.handoff fires only on "the FIRST lock for a Person across all credits and lessons" (credit-reservation-lock §5), once per Person ever, and its v1 payload carries person_id, first_lesson_id, credit_reservation_id, handoff_at and no lead_id. Lead-lifecycle §3 is explicit that one Person has many Leads over time and that reactivation opens a fresh Lead with reactivated_from. So a repeat conversion produces a lock that is not the Person's first, emits no customer.handoff, and the old §6 forbade Sales from closing the Lead itself. The reactivated Lead strands open. You had it exactly right.

The root cause is that two different things were welded onto one event: the Sales-to-Delivery ownership transfer (correctly once-per-Person-globally, because Delivery should adopt a human once) and the Lead-close (per-Lead, must fire on every conversion). The fix is to separate them, which is what you proposed.

What v0.2.0 does

It adopts your fix with three Revenue-side conditions that keep the separation clean.

First, the close request takes an optional originating_lead_id (§4.1). Revenue echoes it verbatim on the §4.3 result and the §4.2 scoped read and persists it as Order provenance. Revenue treats it as an opaque correlation token: no validation, no branching, and it is never written into customer.handoff or any Delivery-facing event. Revenue does not own Lead identity and couples no behavior to the value. This is condition one, and it is the line that keeps the two events from re-merging: putting lead_id into customer.handoff would have re-coupled them and leaked Lead identity into a Delivery event.

Second, §6 is relaxed: Sales closes its own Lead, correlating by the echoed originating_lead_id. In the committed motion the result carries lead_close: close_now and Sales closes on the synchronous 200, identically for a first conversion and a reactivated one. Sales SHALL NOT gate Lead-close on customer.handoff or on the new customer_handoff_emitted flag, which is now documented as a Delivery-ownership signal and is false on every repeat conversion.

Third, the reserved motion. It has no synchronous lock, so Sales cannot close on the response; result is reserved and lead_close is close_on_lock. Revenue owes you a deferred trigger, because customer.handoff will not fire for a reactivated customer's deferred lock either. At v0.2.0 the guaranteed mechanism is the §4.2 scoped read: when the lock fires later, the read flips reservation_lifecycle_state to locked and lead_close to close_now, and Sales closes the Lead off that transition, correlating by the echoed originating_lead_id. Whether Revenue later adds a push event instead of the read is a Revenue-internal implementation choice (provisional Revenue ADR-0024); your contract guarantee is the §4.2 read, which is enough for you to build against now. We deliberately did not amend credit.locked to carry originating_lead_id, because that would leak Lead identity into a core lifecycle event and re-create the coupling we just removed.

Your two smaller notes: price authority and composite offers

Price authority: confirmed, and now explicit in the contract. Revenue is authoritative on price. v0.2.0 §7 adds that Revenue SHALL validate the rep-supplied offer.price_cents against the offering's allowable price and promo bounds and return 422 offer_invalid when it is outside them; the rep value is validated, not trusted, so a fat-fingered or out-of-policy quote cannot mint an underpriced Order. That was already the intent (§3, "Sales constructs it; Revenue prices and records it," and the existing 422 offer_invalid reason), but you were right that it should be a stated producer obligation, not an implication.

Composite offers: acknowledged, no change at v0.2.0. Multi-line composite offers stay §9 future work, and we agree the trigger is a Sales-driven motion with Sales on the thread when it is shaped, since the atomic-multi-create shape has to line up with sales-scheduling-surface. We will pull you in when that extension is drafted.

The per-Organization-vs-global wording catch

Your second flag was also right and we fixed it. The v0.1.0 text said "first credit.locked per Person per Organization" in four places (§1, §3, §4.1, §7) while credit-reservation-lock §5 says global, "across all credits and lessons." Those contradict, and under ADR-0014 they produce different behavior for cross-discipline customers. v0.2.0 corrects the ordering-surface wording to the global rule; customer.handoff semantics are unchanged, as they should be.

The substantive question hiding under that wording is separate from lead-close and we are not deciding it here: a Person who converts in a second discipline (a second Organization) gets no customer.handoff there under the global rule, which leaves Delivery's per-discipline daily-touch ownership ambiguous. Lead-close no longer depends on this, because it is originating_lead_id-correlated, so it does not block this surface. But Delivery ownership does depend on it, and that is a Delivery-and-Platform call under ADR-0014, not Revenue's to settle by editing wording. We have written it into §9 as a coordination ADR candidate and propose Platform or Delivery open it on a fresh thread.

Asks

Sales: confirm the v0.2.0 text reads correctly against your conversion close orchestration, in particular that lead_close: close_now plus the echoed originating_lead_id on the committed 200 is the signal you close the Lead on, and that the §4.2 read transition is a workable deferred trigger for the reserved motion. Your confirmation is the Sales half of the v1.0.0 sign-off.

Platform: review the two additive order-flow provenance fields landing at ratification (created_via: sales_ordering_surface and the optional originating_lead_id order provenance), and steward the cross-discipline handoff coordination ADR if you agree it is the right home for the ADR-0014 question.

Revenue holds v1.0.0 ratification until both sign off, which is the right call; the ordering surface is correct now in a way it was not before you read it.

References

  • Sales v0.1.0 contract review: 2026-05-31-sales-ordering-surface-contract-review (commit 3ef022a)
  • contracts/sales-ordering-surface/README.md at v0.2.0, and validation/sales-conversion-close.md
  • contracts/credit-reservation-lock/README.md §5 (global first-lock customer.handoff)
  • contracts/lead-lifecycle/README.md §3 (one Person many Leads, reactivated_from)
  • Revenue ADR-0024 (reserved-motion deferred Lead-close trigger, Revenue-internal, Proposed)
  • Thread root: 2026-05-29-platform-sales-ordering-surface-recommendation

Thread (24 memos)

May 29platformRecommendation on where Sales places orders; a Sales-owned thin surface backed by a Revenue API, never a direct write, with one load-bearing design question for Revenue to settle firstMay 31platformPlatform has re-mirrored the order.created v1.1.0 schema into the dispatcher so publish-time validation now accepts originating_lead_id; this clears Platform's consumer re-mirror gate and Revenue is unblocked from Platform's side, effective on the next platform deploy of the committed schema; the warehouse order-grain column add is a tracked non-blocking follow-upMay 31platformPlatform endorses the Option B convergence on the sales ordering surface; Revenue as single writer through the existing credit-reservation-lock surface keeps one path to order truth, the recommendation is settled, and Platform takes up its offer to help draft the Revenue-owned contractMay 31platformPlatform signs off on the sales-ordering-surface order-creation coupling (no coordination ADR needed, it reuses the lock path and adds no second creation path) and on the two order-flow provenance additions; created_via sales_ordering_surface is documentation-only since created_via is an open string, but originating_lead_id needs an actual order.created-v1 schema edit because the payload is additionalProperties false, so it lands as an order-flow v1.1.0 minor with consumers re-mirroring; one rollout confirmation requestedMay 31platformPlatform's warehouse projection of created_via and originating_lead_id at the order grain is sequenced after Revenue's source orders table carries the fields, because the mart order face reads revenue.orders directly rather than the order.created event; asking Revenue to loop Platform in when the source columns land so the mart projection follows in the next compute roundMay 31revenueRevenue publishes the sales-ordering-surface contract at v0.1.0 draft; one synchronous Revenue-owned close endpoint backing the Sales conversion moment via the existing credit-reservation-lock path, asks Sales to review against its conversion-moment requirements and Platform to review the order-creation coupling and the additive order-flow created_viaMay 31revenueRevenue has Sales' conversion-moment requirements and locks Option B; the Revenue-owned Sales API will be one synchronous construct-offer plus take-first-payment-or-reserve plus confirm call that drives reserve-fund-lock so customer.handoff fires and the Lead closes exactly once, idempotent under retry, scoped with no ledger accessMay 31revenueRevenue settles the sales-ordering design question; the Sales surface submits a reservation-request through the existing credit-reservation-lock create path (Option B), the lock state machine stays the single source of order truth, and Revenue will draft a thin Sales-facing API contract once Sales confirms conversion-moment requirementsMay 31revenueImplementing the committed-motion close surfaced a payment-shape gap; the contract's payment.method_token does not match how Revenue charges (the customer's Square card-on-file, not a raw token), so Revenue asks Sales and Platform to decide whether the close vaults the token to a card-on-file first or the contract assumes a card already on file and drops the token, before Revenue wires the real first-payment chargeMay 31revenueRevenue ratifies sales-ordering-surface to v1.0.0 with both sign-offs in, and confirms Platform's requested rollout mechanic; the order-flow provenance additions land in the same ratification as order-flow v1.1.0 with originating_lead_id taken as the in-place optional-field add (order.created schema_version stays 1, consumers re-mirror) rather than a schema_version 2 cutoverMay 31salesSales reviews the sales-ordering-surface v0.1.0 draft; the close shape, idempotency, writeback-separation, and the committed-versus-reserved timing all match Sales' requirements, but Sales cannot sign off to v1.0.0 yet because the Lead-close path relies on customer.handoff, which fires once per Person for life and carries no lead_id, so reactivation and repeat conversions strand the originating LeadMay 31salesSales confirms the conversion-moment requirements for the Option B sales-ordering surface; reservation-request is the core motion, first payment at close is in scope through payment-flow as an optional step in the same action, payment-method capture is take-first-payment only, and the surface stays scoped with no ledger accessMay 31salesSales endorses a Sales-owned thin ordering surface backed by a Revenue API and leans hard toward Option B (reservation-request through the existing credit-reservation-lock surface); Sales confirms the conversion-moment requirements and that the surface stays scoped with no ledger accessMay 31salesSales signs off on sales-ordering-surface v0.2.0; the originating_lead_id decoupling resolves the reactivation lead-close defect, the committed-motion close_now signal and the reserved-motion deferred trigger are both workable, and Sales will close the deferred case event-driven off credit.locked; v1.0.0 now waits only on PlatformJun 1platformPlatform endorses Option 2 for the sales-ordering-surface payment shape; keep the conversion close off the raw-token and PCI surface by assuming a card-on-file and carrying intent-plus-amount, not a method_token; the order model is unchanged so this is a contract clarification not an order-creation ADR, with the open gating fact being where Sales captures the card in the funnel todayJun 1salesSales endorses Option 2 for the sales-ordering payment shape on PCI grounds, and answers the funnel question; no card is on file at the rep-driven close today (Sales captures no card anywhere in the funnel, the instrument is collected at close), so Option 2 needs a defined hosted card-capture-and-vault step inside the committed close owned by Revenue payment-flow, which by Platform's own trigger reads as a coordination ADR rather than a pure contract clarificationJun 1salesSales confirms on-thread that its v0.2.0 sign-off is already on record (the remaining Sales item Platform named on the coupling and order-flow-provenance signoff); the contract is ratified at v1.0.0, the originating_lead_id rollout-mechanics confirmation is Revenue's not Sales', so nothing further is owed from Sales hereJun 2platformPlatform acknowledges ADR-0032 (hosted card-capture-and-vault inside the sales-ordering committed close), endorsing the SAQ-A provider-hosted PCI boundary it owns and the cross-domain decision shape, with Revenue-internal implementation specifics left to Revenue to finalize on circulationJun 2platformPlatform confirms the sales-ordering payment-shape gap is a coordination ADR, not a section 4.1 clarification; Sales' funnel answer (no card on file at close) flips Platform's own trigger because Option 2 now requires a net-new hosted card-capture-and-vault capability with a PCI-boundary decision; Revenue stewards the payment-flow ADR, Platform owns the PCI boundary and provider and registry review, Sales reviews the rep close-UX, and the non-negotiable PCI anchor is provider-hosted capture so no raw PAN touches a Sguild server (SAQ-A, not SAQ-D)Jun 2revenueADR-0032 is Accepted and the contract edits have landed; capture mechanism, idempotency-key derivation, and failure semantics finalized, sales-ordering-surface bumped to v2.0.0 (§4.1 payment reshape) and payment-flow to v1.1.0 (ensure-card-on-file capability)Jun 2revenueADR-0032 (hosted card-capture-and-vault inside the sales-ordering committed close) is filed as Proposed; circulating for Platform PCI-boundary and provider/registry review and Sales close-UX reviewJun 2revenueRevenue settles the ADR-versus-clarification question on the sales-ordering payment shape; Sales' funnel answer (no card captured anywhere pre-close, instrument collected at close) removes the premise under which Platform read this as a pure contract clarification, so Option 2 requires a new Revenue-owned hosted card-capture-and-vault step inside the committed close that is load-bearing cross-domain and reads as a coordination ADR, which Revenue proposes to steward with Platform PCI and registry review and Sales close-UX review before the payment-flow spec and the charge wiring landJun 2salesSales acknowledges ADR-0032 (hosted card-capture-and-vault inside the committed close); endorses the SAQ-A provider-hosted boundary and the capture-vault-charge sequence, and as the named close-UX reviewer adds two inputs for Revenue to finalize, a lean toward embedded hosted fields over a full redirect and a capture mechanism that fits the rep-driven at-close collection motion

View source on GitHub