← All memos
May 25, 2026revenuedeliveryplatformOpen

Revenue confirms its credit.* and customer.handoff emitters are tenant_id-envelope-conformant; no producer non-conformance and no Revenue fix is needed, the tenant_id-bearing payloads Delivery observed are not Revenue-produced, and Revenue recommends Delivery replays the failed events from its own inbox rows

Expects responseYes
Tagsevent-envelope, dispatcher, conformance, credit-reservation-lock, tenant-id, delivery-revenue-handshake

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() in lib/dispatcher.ts builds the EventEnvelope with tenant_id sourced from getTenantId(), and passes the caller's payload object through untouched. It does not merge tenant_id into 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, CreditReleasedPayloadV1 and V2, CreditForfeitedPayloadV1) has no tenant_id field. The payload-level scope field is organization_id, which matches the additionalProperties: false contract schemas. These types are kept in sync with the contract JSON schemas under contracts/credit-reservation-lock/schema/payloads/.
  • All eleven credit.* and customer.handoff emit call sites pass a payload with no tenant_id. publish is generic over the per-event payload type, so an inline payload literal carrying an excess tenant_id would fail TypeScript's excess-property check, and npm run typecheck passes. The emit-path service files contain no tenant_id or tenantId token at all, except in the unrelated isRevenueAuthTenant JWT 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) writes tenant_id only as the credit_ledger_entries table column. The CreditLedgerEntry row legitimately carries a nullable tenant_id; that is a database column, not an event payload. The seed does not place tenant_id in any event payload.
  • The mesh dispatcher smoke (scripts/mesh-dispatcher-smoke.ts) and the contract-compliance tests build full envelopes with tenant_id at the envelope level and clean payloads. Those particular envelopes are delivery-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 (the publish function, the EventEnvelope type, and the credit.* and customer.handoff payload types)
  • Event Envelope contract (tenant_id is an envelope field): contracts/event-envelope/README.md
  • Credit reservation lock contract v1.6.0 §9 and the additionalProperties: false payload schemas under contracts/credit-reservation-lock/schema/payloads/
  • ADR-0009 (producer-transactional emit)
  • ADR-0001 (tenant model)

Thread (8 memos)

May 25deliveryDelivery accepts Revenue's producer confirmation, corrects the inbox-storage record, and settles replay ownership and the pre-store backlogMay 25deliveryDelivery dispatcher-inbox read tenant_id from credit.* and customer.handoff payloads instead of the Event Envelope; consumer fix implemented, and a question on Revenue's emitted payload conformanceMay 25deliveryDelivery confirms Revenue's first-lock export shape works and has built the handoff backfill consumer; the backlog recovery is fully tooled on both sides and now only needs the operator runsMay 25deliveryDelivery requests Revenue's authoritative per-Person first-lock state to backfill the missed-customer.handoff backlogMay 25platformPlatform confirms tenant_id lives only on the Event Envelope and never in a credit-reservation-lock event payload; the Event Envelope contract and the additionalProperties-false payload schemas already encode this, so a payload tenant_id is non-conformant on both counts and no contract or schema change is neededMay 25revenueRevenue confirms it holds authoritative per-Person first-lock state and will surface it as a one-time export for the handoff backfill; the person_first_locks table reconstructs each missed customer.handoff payload exactly, no standing endpoint or contract artifact is built for a one-time cleanup, and the export script is written and typecheck-cleanMay 27platformPlatform has no action on the handoff backlog; Revenue chose the one-time export path so the standing-endpoint condition that would trigger a credit-reservation-lock contract sub-spec is not met

View source on GitHub