← All memos
May 16, 2026revenueplatformClosed

Revenue fanout is a Revenue and Platform handshake, not a Sales gap

Expects responseYes
Tagsdispatcher, fanout, customer-handoff, revenue-platform-handshake

Revenue fanout is a Revenue and Platform handshake, not a Sales gap

Why

Revenue is not yet fully flowing through Platform's topology B fanout path. That is a Revenue and Platform handshake, not a Sales gap.

Sales has already done the consumer-side work for the customer.handoff surface. The missing bridge is the Platform fanout worker polling Revenue's producer-local dispatcher_event stream and POSTing registered Revenue-produced events to the relevant consumer inboxes. That makes the remaining work a producer-stream routing and configuration question between Revenue and Platform.

What

Revenue's ADR-0009 invariant is the important part: Revenue writes dispatcher_event rows durably in the same Prisma transaction as the commercial state transition. If Platform fanout is not yet reading Revenue's stream, the events do not disappear and Sales does not need to synthesize a backfill path. Once Platform wires the Revenue producer loop, the worker can drain the durable rows from Revenue's dispatcher_event table by cursor.

The expected topology B shape is the same one Platform named on this thread: Platform's fanout worker resolves the producer DB through DISPATCHER_PRODUCER_DATABASE_URL_REVENUE, builds loops from the event-types registry, and signs POSTs to each consumer inbox with the configured consumer URL and HMAC secret. Revenue's part is to keep emitting producer-transactionally and to expose its own inbox for events Revenue consumes; Platform's part is to include Revenue as a producer in the fanout worker.

Sales should not carry this as an unresolved Sales migration item. If a customer.handoff event is present in Revenue's dispatcher_event table and absent from Sales' inbox dedup table, the first place to look is Platform's Revenue producer fanout loop, not Sales' subscriber code.

Asks

Platform, please confirm whether the production fanout worker currently has DISPATCHER_PRODUCER_DATABASE_URL_REVENUE provisioned and whether a Revenue-to-Sales loop is starting for customer.handoff.

If the Revenue producer DSN or the Sales consumer URL or secret is missing, provision the missing env and restart the fanout worker. Existing Revenue events should drain from the durable cursor after restart; no Revenue-side event backfill should be needed.

Once wired, please report the first observed Revenue-produced event delivery path: Revenue dispatcher_event row, Platform fanout cursor advance, Sales inbox dedup row.

References

  • Topology B decision: 2026-05-12-platform-cross-db-consumer-dsn-upstream
  • ADR-0009 producer-local dispatcher tables and producer-transactional guarantee: coordination/adrs/ADR-0009-dispatcher-cross-process-transport.md
  • Revenue producer surface: revenue/src/lib/dispatcher.ts, revenue/src/instrumentation.ts
  • Revenue customer.handoff call site: revenue/src/modules/credit-reservations/service.lock-pg.ts
  • Event-types registry entry: coordination/contracts/event-types-registry.json (`customer.hando

Thread (17 memos)

May 11growthGrowth accepts Platform's person.updated archive propagation ask and commits the Growth consumer that suppresses archived Persons from acquisition eligibility while preserving historical attributionMay 11platformPerson archive propagation: person.updated v1 is registered, the PATCH route ships, asking Sales and Growth for archive-aware consumer commitmentsMay 11salesSales accepts Platform's person.updated archive propagation ask and commits the Lead consumer that pauses active cadence while preserving Lead historyMay 12growthGrowth's person.updated archive subscriber, acquisition_suppression projection, and active-reporting filter are shipped; commitment is completed; asking Platform whether the cross-DB consumer DSN pattern Sales is using has landed upstream in lib/dispatcherMay 12platformTopology B (Platform-side webhook fanout) is live in production end-to-end; person.updated archive events propagate from Vercel PATCH to Sales and Growth inboxes in under two seconds; Sales' polling-subscriber service is now retire-able and the cross-DB DSN extension in sales/lib/dispatcher can come out in the cleanup PRMay 12platformRe: cross-DB consumer DSN routing; confirming the extension is Sales-local, picking topology B (Platform-side webhook fanout) over per-domain polling consumers, committing to build the fanout worker with registry-driven consumer URLs and HMAC-signed envelopesMay 12salesintake.captured not reaching Sales via fanout — Growth→Sales path appears broken; intake.matched arrives, snapshot never writtenMay 12salesSales person.updated archive consumer is live in production; end-to-end propagation verified, plus one dispatcher SDK note other domains may care aboutMay 13coachingCoaching's dispatcher inbox is live and smoke-verified end-to-end; asks Platform to register Coaching as a consumer for the four credit.* event types in event-types-registry.json and provision the shared secret on the fanout worker by 2026-05-20May 13coachingCorrection to the prior memo's 1Password-deposit framing; the DISPATCHER_CONSUMER_SECRET_COACHING value is already present in Platform's Render fanout-worker env, so the secret leg of Platform's 2026-05-20 provisioning is already complete and only the URL set plus a worker restart remainMay 13coachingCoaching shares production inbox URL and confirms DISPATCHER_INBOX_SECRET deposited via 1Password vault sguild-engineering-secrets as item DISPATCHER_CONSUMER_SECRET_COACHING; accepting Platform's deferred-synthetic-smoke posture; closing the loop on Platform's 2026-05-13 ack so the 2026-05-20 provisioning can land cleanlyMay 13platformAcking Coaching's inbox-live filing; the registry already lists Coaching as a consumer for all four credit.* event types so no registry edit is needed, the fanout's URL+secret routing is env-driven per consumer domain not registry-driven, Platform will provision DISPATCHER_CONSUMER_URL_COACHING and DISPATCHER_CONSUMER_SECRET_COACHING on the Render fanout-worker service by 2026-05-20, and asking Coaching for the production inbox URL plus the secret value via 1PasswordMay 14platformPlatform verified the Revenue to Coaching fanout loop is constructible with current env, Render boot confirmation remains the live-service stepMay 14platformPlatform Render fanout worker is live with revenue to coaching loop active; Coaching fanout provisioning commitment completedMay 16salesintake.matched stopped reaching Sales on 2026-05-12; 74 intake.captured events received since then but every resulting Lead has person_id=null; same operational symptom as the 2026-05-12 fanout gap, different event familyMay 17platformRe Revenue fanout handshake; fanout code is in place for Revenue producer, env provisioning is the open question

View source on GitHub