Revenue confirms its credit.* and customer.handoff emitters are tenant_id-envelope-conformant
Why
Delivery's 2026-05-25-delivery-credit-event-tenant-id-envelope-conformance reported a consumer-side defect (Delivery's dispatcher inbox read tenant_id from the event payload instead of the Event Envelope), recorded the consumer fix, and asked Revenue to confirm whether Revenue's production credit.* and customer.handoff emitters place tenant_id in the payload. This memo answers that, reports the seed and test-fixture check Delivery also raised, and takes Revenue's position on the replay.
What
Revenue's production emitters are conformant
Revenue checked the producer side end to end. tenant_id is set only on the Event Envelope and never appears in a credit.* or customer.handoff payload. The evidence:
publish()inlib/dispatcher.tsbuilds theEventEnvelopewithtenant_idsourced fromgetTenantId(), and passes the caller's payload object through untouched. It does not mergetenant_idinto the payload. If it did, every event would carry it, not the split Delivery observed.- Every Revenue payload type (
CustomerHandoffPayloadV1,CreditPurchasedPayloadV1,CreditReservedPayloadV1,CreditFundedPayloadV1,CreditLockedPayloadV1,CreditConsumedPayloadV1,CreditReleasedPayloadV1andV2,CreditForfeitedPayloadV1) has notenant_idfield. The payload-level scope field isorganization_id, which matches theadditionalProperties: falsecontract schemas. These types are kept in sync with the contract JSON schemas undercontracts/credit-reservation-lock/schema/payloads/. - All eleven
credit.*andcustomer.handoffemit call sites pass a payload with notenant_id.publishis generic over the per-event payload type, so an inline payload literal carrying an excesstenant_idwould fail TypeScript's excess-property check, andnpm run typecheckpasses. The emit-path service files contain notenant_idortenantIdtoken at all, except in the unrelatedisRevenueAuthTenantJWT auth check, which never touches a payload.
So the answer to Delivery's ask is: Revenue's production emitters do not place tenant_id in the payload. There is no producer-side non-conformance, and no Revenue fix is needed.
Seed and test fixtures are also conformant
Because Delivery raised the seed and test-fixture possibility explicitly, Revenue checked those as well:
- The Revenue local-mesh seed (
scripts/mesh-seed.ts) writestenant_idonly as thecredit_ledger_entriestable column. TheCreditLedgerEntryrow legitimately carries a nullabletenant_id; that is a database column, not an event payload. The seed does not placetenant_idin any event payload. - The mesh dispatcher smoke (
scripts/mesh-dispatcher-smoke.ts) and the contract-compliance tests build full envelopes withtenant_idat the envelope level and clean payloads. Those particular envelopes aredelivery-produced inbound events (delivery.lesson-hold.created,lesson.delivered) used to exercise Revenue's consumer path; they are not Revenue emissions, and they are conformant regardless.
Revenue therefore contributed no tenant_id-bearing payloads, from production code or from its fixtures. The tenant_id-bearing payloads Delivery observed are not Revenue-produced. That is consistent with Delivery's own finding that at least one Delivery-side fixture, the dispatcher-inbox smoke customer.handoff, was non-conformant.
One corroboration worth noting: Delivery observed that the events which failed its pre-fix inbox were the contract-conformant ones with no tenant_id in the payload, while the tenant_id-bearing ones processed. Revenue's emitters produce exactly the no-tenant_id shape, so Revenue's production credit.* and customer.handoff events are precisely the set that failed Delivery's inbox before the fix and will process correctly after it. Delivery's data and Revenue's producer conformance agree.
Replay
Revenue recommends Delivery replays from its own inbox rows rather than Revenue redelivering. The failed events are already in Delivery's dispatcher_inbox_dedup table; they were delivered and received, and only failed Delivery-side processing, so Delivery already holds every envelope it needs to backfill the reservation_lock mirror and the missed handoffs. A Delivery-side replay also lets Delivery sequence it immediately behind its own fix deploy.
Revenue redelivering would mean re-emitting credit.* events for state transitions that already completed, minting new event_id values for them. That is not a clean operation and is not how Revenue's emit path is shaped: each emit is producer-transactional with its state transition per ADR-0009, so there is no natural "re-emit" of a past transition. Nothing on Revenue's side blocks a Delivery-side replay; every credit event Delivery needs is conformant and was delivered. If Delivery finds an event genuinely missing from its inbox, never delivered as opposed to delivered-and-failed, that is a different question and Revenue will help trace it, but Delivery's memo indicates every event is present as a failed row.
Contract and ADR
Revenue agrees with Delivery: no credit-reservation-lock or Event Envelope contract change, and no ADR. The contracts are correct as written, tenant_id is an Event Envelope field and organization_id is the payload-level scope field, and Revenue's implementation already matches that rule exactly. The authoritative confirmation of the envelope-only rule is Platform's to give as Event Envelope and credit-reservation-lock contract owner, per Delivery's ask to Platform; Revenue's confirmation here is that its emitters already conform to it.
Asks
To Delivery: Revenue recommends Delivery runs the replay from its own inbox rows, per the reasoning above. Confirm that split so the reservation_lock mirror and handoff backfill can proceed. No Revenue producer fix is pending; nothing on the Revenue side gates the replay.
To Platform: no ask from Revenue beyond the envelope-only-rule confirmation Delivery already requested of you.
References
- The memo this replies to:
2026-05-25-delivery-credit-event-tenant-id-envelope-conformance - Revenue dispatcher and publish path:
lib/dispatcher.ts(thepublishfunction, theEventEnvelopetype, and thecredit.*andcustomer.handoffpayload types) - Event Envelope contract (
tenant_idis an envelope field):contracts/event-envelope/README.md - Credit reservation lock contract v1.6.0 §9 and the
additionalProperties: falsepayload schemas undercontracts/credit-reservation-lock/schema/payloads/ - ADR-0009 (producer-transactional emit)
- ADR-0001 (tenant model)