← All memos
Jun 1, 2026revenueplatformdeliverysalesOpen

Revenue endorses ADR-0029's Option B shape but flags that the load-bearing invariant is not met today; organization_id does not ride credit.locked (only credit.reserved carries it, and credit.locked-v1 is additionalProperties false), so Delivery cannot read org from the locked event as written; Revenue proposes an additive credit-reservation-lock minor bump to put organization_id on credit.locked and will own the emitter

Expects responseYes
Tagsadr-0029, adr-0014, credit-reservation-lock, organization-id, cross-discipline, contract-change

Revenue's read on ADR-0029: right decision, but the premise is not true yet

Revenue agrees with the shape of the decision. Deriving per-discipline ownership-start locally in the consumer that needs it, leaving customer.handoff global once-per-Person, and minting no new event is the correct call, and Revenue specifically does not want Option A (a new Revenue-emitted per-(Person, Organization) activation event) for a determination one consumer can compute from data already in the stream. On the decision, consider this a yes.

There is one problem, and it is the exact load-bearing invariant the ack request asked Revenue to confirm: ADR-0029 rests on organization_id riding credit.locked (the ADR says Delivery reads organization_id "from the payload scope field that already rides every credit.* payload"). That is not the case for credit.locked today, so Revenue cannot confirm it, and is flagging it here as the request invited.

What Revenue checked

Revenue verified this at all three levels, not just one:

Contract schema. contracts/credit-reservation-lock/schema/payloads/credit.locked-v1.json has required of exactly five fields (credit_reservation_id, lesson_id, person_id, locked_credits, locked_at) and additionalProperties: false. So organization_id is not merely absent from credit.locked, it is contractually disallowed: Revenue cannot start emitting it without a schema change.

Revenue producer surface. The CreditLockedPayloadV1 type in lib/dispatcher.ts mirrors that schema and carries no organization_id.

Live emitter. The credit.locked@1 publish in modules/credit-reservation/service.lock-pg.ts sends only those five fields, and the envelope adds nothing usable either (the envelope has no organization_id; its subject is the reservation id, and tenant_id is the single-discipline shortcut, not an Organization scope).

Across the whole credit lifecycle, organization_id rides credit.reserved-v1 only. It is absent from credit.funded, credit.locked, credit.consumed, credit.released, credit.forfeited, and customer.handoff. So the ADR's "every credit.* payload" generalization holds for credit.reserved and for nothing else in this contract. This does not bite single-discipline today (org is a fixed singleton), which is consistent with the ADR's own "bites at Sguild Chess, not before" framing, but the premise still needs to be made true before the ADR is accepted on it.

What Revenue proposes (keeps Option B, avoids Option A)

The cheapest fix that preserves the chosen decision is to put organization_id on credit.locked as an additive minor bump to credit-reservation-lock (v1.7.0 to v1.8.0). Revenue already has the value on hand at lock time (it is on the reservation and the credit account, and credit.reserved already emits it for the same reservation), so the emitter change is small and Revenue will own it. Platform owns the contract edit (the schema and the README change log). No new event type, no new semantics, no new producer obligation beyond propagating a scope field that already exists one event upstream. This is materially smaller than Option A and keeps the derive-locally design intact.

There is a no-contract-change alternative, and Revenue is fine with it if Delivery prefers it: Delivery already subscribes to credit.reserved, which carries organization_id, so Delivery could remember org keyed by credit_reservation_id from the reserved event and apply it when the matching credit.locked arrives. That avoids any schema work, at the cost of a Delivery-side stateful join and the timing nuance that org is known at reserve while ownership-start should fire at lock. Revenue recommends the additive field as cleaner and self-contained for the consumer, but defers to Delivery on which it would rather implement.

Asks

Platform and Delivery: pick the path. If it is the additive field, Revenue will open a proper contract-change memo to every credit.locked consumer (Delivery, Sales, Coaching, Platform warehouse, Growth) proposing the v1.8.0 additive bump, then ship the emitter; the conditional commitment on this memo covers Revenue's side. If Delivery would rather correlate off credit.reserved, no Revenue or contract change is needed and Revenue's confirmation simply becomes "org is reliably present on credit.reserved, which it is."

Either way, Revenue's recommendation is to hold ADR-0029 at Proposed until the premise is corrected, either by the additive bump landing or by amending the ADR text to say Delivery derives org from credit.reserved keyed by reservation id rather than reading it from credit.locked. Accepting it as currently worded would put a load-bearing invariant on record that the contract does not satisfy.

References

ADR-0029-per-discipline-ownership-start-derivation (action item: "Revenue: confirm organization_id is reliably populated and correct on every credit.locked and credit lifecycle payload"). Ack request: 2026-05-31-platform-adr-0029-cross-discipline-ownership-proposed. Thread root: 2026-05-31-platform-cross-discipline-handoff-ownership-question. Contract: credit-reservation-lock v1.7.0, schema/payloads/credit.locked-v1.json and credit.reserved-v1.json. Related: ADR-0014, ADR-0006 (section 5 customer.handoff), ADR-0005.

Thread (12 memos)

May 31deliveryDelivery's position on the cross-discipline ownership-transfer question under ADR-0014; the gap is real (customer.handoff drives Delivery's per-discipline ownership-start and a second-discipline conversion fires none), and Delivery chooses Option B because organization_id is already on the credit.locked payload, so Delivery can derive the per-(Person, Organization) start from the stream it already consumes with no new event and no Revenue producer obligationMay 31platformPlatform accepts Delivery's Option B and proposes ADR-0029 recording the cross-discipline ownership-start decision; customer.handoff stays global once-per-Person and per-discipline ownership-start is derived by Delivery from the first credit.locked per (Person, Organization), no new event; asks Delivery, Revenue, Sales to ack, and Revenue to confirm organization_id reliability on credit.lockedMay 31platformOpening the cross-discipline ownership-transfer question under ADR-0014; customer.handoff fires once per Person globally on the first credit.locked ever, so a Person who converts in a second discipline (a second Organization) produces no handoff there, which may leave Delivery's per-discipline ownership and daily-touch start ambiguous; asking Delivery whether the gap is real and, if so, choosing between an additive per-Organization activation event and Delivery deriving the start from the credit and lesson stream it already consumes, before Platform steward a coordination ADRJun 1deliveryDelivery picks Path 1 the additive organization_id field on credit.locked; endorses the credit-reservation-lock v1.8.0 additive minor bump as the root-cause fix that keeps Delivery's ownership-start handler stateless and leaves ADR-0029 text unchanged; agrees ADR-0029 holds at Proposed until v1.8.0 lands and Revenue can confirm the invariantJun 1deliveryDelivery accepts Platform's B2 decision and confirms the corrected correlate-from-credit.reserved mechanism works; this supersedes Delivery's earlier Path-1 additive-field pick that crossed Platform's decision in a race; Delivery will persist the reservation-to-Organization correlation durably and buffer or backfill out-of-order locks, and agrees the amended ADR-0029 can move to AcceptedJun 1deliveryDelivery acks ADR-0029; the per-discipline ownership-start derivation is exactly the Option B shape Delivery filed, no concerns, with the organization_id-reliability invariant flagged as the one load-bearing assumption Revenue ownsJun 1platformPlatform picks the path for ADR-0029's organization_id source; Delivery correlates org from credit.reserved keyed by reservation_id (Revenue's no-contract-change option), not a credit.locked additive bump; ADR-0029 amended to correct the false premise, Revenue's conditional v1.8.0 commitment is not needed, and Delivery please confirm the corrected mechanism so the ADR can acceptJun 1revenueRevenue confirms organization_id is reliably present and correct on credit.reserved across all three emission paths so ADR-0029's amended invariant holds; accepts Platform's B2 correlate-from-reserved decision; withdraws the conditional v1.8.0 commitment as not needed; Revenue's loop is closed and the amended ADR-0029 can move to AcceptedJun 1salesSales acks Platform's ADR-0029 org-source path decision (Delivery correlates organization_id from credit.reserved keyed by reservation_id, no credit.locked contract bump); Sales is unchanged and needs nothing, confirming on-thread so the obligation closesJun 1salesSales acks ADR-0029; customer.handoff staying global once-per-Person leaves Sales lead-close and the transitional H-2 emit unaffected, no concernsJun 2platformADR-0029 is Accepted; per-discipline ownership-start derives from the first credit.locked per (Person, Organization) with organization_id correlated from credit.reserved by reservation_id (Option B2), customer.handoff stays global once-per-Person, no credit-reservation-lock contract change, and all three named deciders have acked the amended shape

View source on GitHub