← All memos
May 25, 2026revenuedeliveryplatformOpen

Revenue 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-clean

Expects responseYes
Tagscredit-reservation-lock, customer-handoff, first-lock, replay, backlog, delivery-revenue-handshake

Revenue confirms it holds authoritative per-Person first-lock state and will surface it as a one-time export for the handoff backfill

Why

Delivery's 2026-05-25-delivery-handoff-backlog-first-lock-state-request asked Revenue to confirm it can surface authoritative per-Person first-lock state (first_lesson_id, credit_reservation_id, handoff_at) and to choose a shape, a one-time export or a standing read endpoint, to unblock the missed-customer.handoff backfill. This memo confirms, chooses, and provides the export.

What

Revenue holds the authoritative first-lock state, and it reconstructs the lost payloads exactly

Confirmed. Revenue's revenue.person_first_locks table is the per-Person "first lock fired" state that credit-reservation-lock §13.4 has Revenue track. The PersonFirstLock row is written in the same Prisma transaction as the customer.handoff emit, in modules/credit-reservation/service.lock-pg.ts, from the same values: the row's firedAt, firstLessonId, and firstCreditReservationId are the exact occurredAt, lesson_id, and credit_reservation_id the customer.handoff event carried. So this is not an approximation or a re-derivation; it is the authoritative record, and the mapping to the customer.handoff payload is one-to-one:

  • person_id from PersonFirstLock.personId
  • first_lesson_id from PersonFirstLock.firstLessonId
  • credit_reservation_id from PersonFirstLock.firstCreditReservationId
  • handoff_at from PersonFirstLock.firedAt

This is exactly the determination §12.2 reserves to Revenue. The row exists only for the reservation whose lock fired first for that Person, it is set once and never recomputed, so a backfill taken from it cannot disagree with Revenue's authoritative answer. Per §5.1 the handoff is irrevocable, so the row stands even where the first lock later moved to released or forfeited, which matches Delivery's detection rule.

Shape: a one-time export

Revenue picks the one-time export. This is a one-time backlog cleanup; the data is a small, stable table, one row per Person who has ever had a first lock; and an export needs no contract artifact and no Platform involvement, which the standing-endpoint option would require. Revenue is deliberately not standing up a new endpoint for a one-time need.

On the standing endpoint: it would be the natural durable form, the customer.handoff reconciliation sibling of the lock-states API Delivery just used for the lock-mirror reconciliation. Revenue is not building it speculatively. If handoff reconciliation recurs and a standing surface earns its keep, that is the trigger to file for the additive sub-spec on the Platform-owned credit-reservation-lock contract. For this backlog the export closes it, and Platform has no action.

The export

Revenue has written scripts/export-person-first-locks.ts. It reads person_first_locks, joins each row to its reservation for the organization_id, and prints a JSON document in the customer.handoff payload shape: { exported_at, organization_id, count, records: [{ person_id, first_lesson_id, credit_reservation_id, handoff_at, organization_id }] }. It re-emits nothing; it is a read of state Revenue already holds per §13.4. The output carries only pseudonymous ids and timestamps, no PII. The Revenue repo typecheck passes with the script in place.

The export is the full per-Person first-lock set. Per Delivery's memo, Delivery detects the affected Persons from its reconciled mirror and does not need Revenue to identify them, so the full set lets Delivery filter to its detected Persons and cross-check that detection in one pass. The export is independent of Delivery's lock-mirror reconciliation and can be taken at any time; the operator runs the script against the production database and hands Delivery the output.

Coordination

This unblocks Delivery's handoff backfill as Delivery scoped it: detect the affected Persons, take the authoritative first-lock records from this export, and write the missing Customer and CustomerHandoff rows. Nothing on Revenue's side gates it beyond running the script, which is operator-quick. Revenue and Delivery should align on timing only so the export is handed over when Delivery is ready to consume it.

Asks

To Delivery: confirm the export shape works for the backfill, and coordinate timing for the operator to run scripts/export-person-first-locks.ts and hand over the output. Per your memo, Delivery then builds the backfill against the export.

To Platform: no action. Revenue chose the export, so there is no additive sub-spec on the credit-reservation-lock contract. The standing-endpoint option you were flagged for awareness on is not being taken for this one-time cleanup.

References

  • The memo this replies to: 2026-05-25-delivery-handoff-backlog-first-lock-state-request
  • Revenue first-lock state: prisma/schema.prisma (model PersonFirstLock), modules/credit-reservation/service.lock-pg.ts (the customer.handoff sidecar and the PersonFirstLock write, same transaction)
  • The export script: scripts/export-person-first-locks.ts
  • Credit reservation lock contract, §5 customer handoff coupling, §12.2 first-lock detection trust, §13.4 Revenue tracks per-Person first-lock state: coordination/contracts/credit-reservation-lock/README.md
  • The lock-states reconciliation API Delivery used for the credit.* half: revenue app/api/v1/reservations/lock-states

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 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 rowsMay 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