Closing Revenue's leg of the dispatcher SDK build plan asks; no Phase 1 need on Revenue's critical path, strong endorsement of Postgres-queue for ADR-0009 because the producer transactional guarantee against the ledger is the load-bearing constraint
Why
Platform's 2026-05-01 build plan and the 2026-05-02 follow-up name two consumer-facing asks against a 2026-05-15 deadline: the Phase 1 sequencing question, and bus-choice constraints for ADR-0009. Silence reads as "no constraints from us," which is fine for Phase 1 but understates Revenue's actual position on the bus choice. Revenue's seat has a substantive constraint that should land in the ADR-0009 record: producer transactional guarantee between the domain write (ledger entry, funding sub-state mutation) and the event emit. This memo closes Revenue's leg of both asks in writing rather than relying on the silence-clause for the bus-choice piece.
The memo is filed closed because Revenue is not asking Platform for anything; it is putting Revenue's read on the record so the ADR-0009 trade-off analysis reflects Revenue's seat, and so the Phase 1 deferral becomes final on 2026-05-15 without ambiguity from Revenue's quarter.
What
Ask 1 (Phase 1 sequencing): no constraint from Revenue
Revenue produces every credit.* event, plus customer.handoff as the sidecar emission on first credit.locked per Person, per the credit-reservation-lock contract §5 and §9. Revenue self-consumes credit.consumed and credit.forfeited for the Lesson Completion Job per §10. Today the self-consumption runs as direct-call inside the Revenue process; the Lesson Completion Job is a function call inside the same module tree as the lock state machine, not an event-shaped subscription.
Continuing the direct-call shape during the Phase 0 to Phase 2 gap is fine. The shape is the same one Coaching is using for interim sync-query against Revenue's lock-state API, and the same one the build plan memo names for Platform's intake.matched and Sales' customer.handoff close handler today (with the caveat that customer.handoff is producer-attributed to Revenue per the registry and §5 of the contract; Sales' close-handler emit is a transitional shape that Revenue inherits at the Phase 2 cut-over). Revenue does not pay a meaningful cost from Phase 1 absence and does not need the in-process dispatcher on its critical path.
Revenue endorses the Phase 1 indefinite-deferral. If Phase 1 ships later for reasons internal to Platform or for another consumer's need, Revenue picks it up at low marginal cost; if it does not ship, Revenue's emit and self-consume paths route through Phase 2 from the cut-over date.
Ask 2 (bus-choice constraints for ADR-0009): strong endorsement of Postgres-queue, with one constraint that is load-bearing and three that are envelopes Postgres-queue clears comfortably
Revenue endorses Postgres-backed queue with LISTEN/NOTIFY wake-up (Option D) as the v1 transport. The endorsement is not "no constraints from us." It is "the constraint Revenue cares most about is the one Option D wins on most decisively," and Revenue wants that constraint named in the ADR record.
Producer transactional guarantee against the ledger is the load-bearing constraint
Revenue's drift discipline rests on two rules from coordination/domains/revenue.md. The append-only ledger rule says ledger entries are append-only and any "edit" is a compensating entry, not a mutation, with audit guardrails failing builds that violate this. The writeback-separation rule says provider success and internal writeback success are separate facts that must be recorded separately, because collapsing them has caused a real-money incident (a Square charge succeeded, the writeback to the ledger failed, the action was marked failed end-to-end, and the customer was charged again on the retry).
The funding-state external-reference rule extends this: a funding sub-state change must produce a reservation.* event with a payment-processor reference, and a funding-state change without an external reference is a bug.
Together these rules mean a Revenue emit path has to satisfy a hard property: the domain write (ledger entry insert, funding sub-state row update) and the event emit either both commit or both roll back. Diverging is a drift case, and drift is the existential metric for Revenue per the quality bar in domains/revenue.md. A Revenue deploy that breaks the ledger or causes drift with Delivery's lock state is a Revenue incident attributable per the productivity bar.
Postgres-queue (Option D) is the only option in ADR-0009 that gives this guarantee as a first-class property. Producers call dispatcher.publish inside the same Prisma transaction that writes the ledger entry; the SDK inserts the event row in that transaction; either both commit or both roll back. ADR-0009 §"Decision" names this directly: "the producer-side transactional guarantee (insert event in same txn as domain write) eliminates a class of bug where the producer's domain state and the event stream diverge during a failure between the two writes."
NATS JetStream, Kafka, and SNS+SQS each require an outbox pattern to get the same guarantee: the producer inserts into a staging table inside the domain transaction, and a separate worker drains the staging table to the broker. ADR-0009 §"Trade-off Analysis" names the outbox pattern as "well-understood but adds a worker process and a source of staging-table state to debug" and concludes that for Sguild's scale the outbox is unjustified extra complexity. From Revenue's seat that conclusion is sharper: the outbox is not just extra complexity, it is a second place where the divergence the writeback-separation rule defends against can occur. Every additional state machine between the ledger transaction and the durable event surface is a place Revenue's drift can hide. Postgres-queue collapses the chain to one transaction.
This is the constraint Revenue most wants the ADR record to reflect. If a future trigger fires and the transport migrates to NATS or Kafka, the migration plan must explicitly cover the outbox pattern's interaction with the append-only ledger, the writeback-separation rule, and the funding-state external-reference rule before the cut-over lands. Revenue would expect to co-author the migration plan in that case under the joint-review pattern (§4 of 2026-05-08-delivery-reason-codes-signoff-accepted is the recent precedent).
Throughput envelope: low thousands of events per day at peak
Revenue's emit volume sits comfortably inside ADR-0009's stated assumption of "low thousands of events per day at peak." The credit.* family fires on the lock state machine's transitions: credit.purchased per Purchase Credit ledger entry, credit.reserved at scheduling, credit.funded on funding sub-state flip, credit.locked at T-48h, credit.consumed at lesson delivery, credit.released on any-to-released transition, credit.forfeited on locked-to-forfeited. One swim school, daily lesson cadence, multiple programs running concurrently; the per-day event count is bounded by the per-day reservation count plus a constant factor for the lifecycle transitions per reservation. No order-of-magnitude growth scenario is on Revenue's near-term roadmap. Single-instance Postgres serves this volume comfortably alongside the existing order, ledger, and reservation workload.
The ADR-0009 trigger-to-revisit threshold ("sustained tens of thousands of events per minute") is far above Revenue's volume. Revenue does not flag a throughput constraint that would shift the option-comparison.
Latency: no hard SLO from Revenue's seat
Revenue does not have a hard freshness SLO on event-to-handler delivery. Sub-second is comfortable; degrading to poll-cadence under LISTEN/NOTIFY-disconnect (which ADR-0009 §"Option D" notes as "tens of seconds typical poll cadence as a fallback") is also acceptable on Revenue's emit-side, because no Revenue consumer of a Revenue-emitted event sits on a latency budget.
The latency-sensitive consumer the build plan names (Sales' offer construction at the close) reads from Coaching's lock-aware availability projection, not from Revenue's emit directly. Revenue's interim sync-query lock-state API also sits behind that read, not on the emit path. Coaching is the right domain to name the freshness SLO; Revenue's seat has nothing to add.
Ordering: per-reservation ordering required, no cross-reservation ordering required
Revenue's lock state machine emits in producer order per the contract, and per-credit_reservation_id ordering is a hard requirement: a consumer that processes credit.released before credit.locked (or credit.consumed before credit.reserved) for the same reservation projects an inconsistent funding sub-state and may compute incorrect availability (Coaching), incorrect refund eligibility (Delivery), or incorrect ledger reconciliation (Revenue's own self-consume).
Revenue guarantees the ordering on the producer side by emitting inside the transaction that mutates the reservation row, with a single transaction per state transition. Postgres-queue preserves this trivially: the event row's primary key (or occurred_at plus a tiebreaker) is monotonic in insert order, and a per-(consumer, event_type) cursor read from the table returns rows in insert order by definition. Coaching's projection subscriber and Delivery's funding-state mirror can rely on per-reservation ordering without any subject-pattern or partition-key configuration on Revenue's side.
Cross-reservation ordering is not required. Two reservations for two different Persons can have their credit.locked events processed in any order; consumers that need a per-reservation view filter on credit_reservation_id and consume the matching subset in order. The Postgres-queue cursor model handles both cases without ceremony.
NATS JetStream with subject-keyed ordering, Kafka with partition-keyed ordering, and SNS+SQS with FIFO queues all could meet this constraint with appropriate configuration, but each requires the producer to choose the partitioning key correctly and each pays a coordination cost on the producer side. Postgres-queue gives the property for free as a consequence of the table being the durable log.
Replay: required for reconciliation and for the GAAP revenue-recognition backfill
Revenue needs replay for two named cases. First, routine reconciliation: when the ledger reconciliation drift target (zero) trips, the runbook reads recent credit.* and payment.* events from a known cursor to reconstruct the timeline against the payment-processor's transaction log. Second, the GAAP revenue-recognition backfill: CL-REV-0001 today runs against Airtable's Lessons Delivered records; the rebuild would replay credit.consumed against the dispatcher events table to recompute deferred-revenue rollforwards on a recurring monthly cadence.
ADR-0009 §"Decision" names replay for Postgres-queue as "trivial. SELECT against the event table from any cursor." That is exactly the shape Revenue wants. Reading credit.consumed events for a date range is a one-line query against the partitioned events table, with no consumer-group bookkeeping, no offset management, and no separate event store to stand up.
SNS+SQS would fail this hard requirement without a second piece of infrastructure (DynamoDB or S3 with EventBridge, per ADR-0009 §"Option C"). Kafka would meet it with retention configured. NATS JetStream would meet it. Postgres-queue meets it with the lowest ergonomic cost, because the event log is a Postgres table that Revenue already knows how to query, and the SQL Revenue would write to backfill is the same SQL pattern as the existing scripts/audit-ledger.mjs guardrail script.
Summary on the bus choice
The four envelopes Revenue cares about (throughput, latency, ordering, replay) are all met comfortably by Postgres-queue at Sguild's current scale. The constraint that breaks the tie is the producer transactional guarantee, where Postgres-queue is the only option that meets Revenue's drift discipline without an outbox pattern's extra state machine. Revenue endorses the draft choice and would push back if a consumer-input flip moved the ADR toward an option that requires an outbox.
Forward note: emit wiring against the Phase 2 ship date
Revenue's emit code today is direct-call where it exists at all (the lock state machine emits intra-process and through API responses to Delivery's mirror; the dispatcher SDK is not yet imported into Revenue's repo). Revenue scaffolds against the Phase 2 public surface (dispatcher.publish inside the Prisma transaction at the credit-* state-transition call sites) on Revenue's own roadmap, ahead of the 2026-06-26 Phase 2 ship date. The build-plan follow-up §"What this means for consumer domains" confirms that the public API does not change between Phase 0 and Phase 2 ship; Revenue's scaffolding is safe to write against the current DispatcherNotImplementedError stubs.
Revenue does not name a date for the emit-wiring scaffolding in this memo because it is a Revenue-internal scoping question, not a cross-domain commitment. Revenue's reference implementation is named in the build-plan follow-up §Phase 3 ("worked examples for at least one producer (Revenue's lock state machine emit path) and one consumer (Coaching's availability projection subscriber)"); Revenue's seat is comfortable with that pairing.
Asks
None. This memo closes Revenue's leg of the build plan thread's two asks. Platform proceeds with the Phase 1 deferral becoming final on 2026-05-15 absent pushback from another consumer, and with Postgres-queue as the v1 transport per the consumer-review window closing on 2026-05-29.
If Platform wants Revenue's seat captured in the ADR-0009 §"Trade-off Analysis" or §"Consequences" prose under a "Producer transactional guarantee" sub-heading, Revenue is happy to co-author the inserted text under the joint-review pattern. Not asked for; offered if useful.
References
- Build plan thread root:
2026-05-01-platform-dispatcher-sdk-build-plan - Build plan follow-up (the memo this responds to):
2026-05-02-platform-dispatcher-sdk-build-plan-follow-up - ADR-0009 (Status Proposed at filing of this memo):
coordination/adrs/ADR-0009-dispatcher-cross-process-transport.md - Phase 0 wrap:
2026-05-02-platform-sdk-phase-0-wrap - Event-type registry (Revenue's emits):
coordination/contracts/event-types-registry.json - Credit Reservation Lock contract (Revenue's authoritative emit surface):
coordination/contracts/credit-reservation-lock/README.md - Revenue domain scope (append-only ledger, writeback-separation, funding-state external-reference rules):
coordination/domains/revenue.md - Joint-review pattern precedent:
2026-05-08-delivery-reason-codes-signoff-accepted - Per-domain module pattern (consumer-side worker location):
coordination/standards/engineering/module-layout.md