← All memos
May 12, 2026salesplatformgrowthFYI

Sales person.updated archive consumer is live in production; end-to-end propagation verified, plus one dispatcher SDK note other domains may care about

Tagsidentity, archive-propagation, dispatcher, lead-lifecycle, fyi

Sales person.updated archive consumer is live in production; end-to-end propagation verified, plus one dispatcher SDK note other domains may care about

What landed

The Sales person.updated subscriber is running in production against Platform's public.dispatcher_event over the DISPATCHER_PRODUCER_DATABASE_URL_PLATFORM connection per the 2026-05-07 stream-visibility response memo. Sales-owned cursor, dedup, and dead-letter rows live in the sales schema as ADR-0009 specifies.

Verification snapshot from the first hour after the worker flip:

  • sales.dispatcher_cursor row for consumer sales:person.updated, last_seq = 7, updated_at = 2026-05-12T04:06:42Z.
  • sales.dispatcher_dedup: 2 rows. Sales received 7 person.updated envelopes from the existing Platform backlog and applied archive semantics to 2 of them; the other 5 were name edits, is_minor flips, or archive -> active transitions that the handler correctly short-circuited per the contract's changed_fields and person.status filter.
  • sales.dispatcher_dead_letter: 0 unresolved. End-to-end execution was clean against real envelopes.

The two archived Persons had their Sales Leads transitioned to cadenceProfile = paused, disposition = terminal_archived, conversionState = none, nextTextFollowUpAt = null, and any pending callback nulled via the canonical updateLeadCallbackReference seam. Lead.stage and history rows are intact; a LeadActivity row with activityType = disqualified was stamped on each Lead with the event_id, person_id, prior_status, and pre-update CRM-state snapshot for audit. Idempotency on re-delivery is verified by the handler's check on (cadenceProfile, disposition) before re-applying.

The Sales commitment on 2026-05-11-sales-person-archive-consumer-commitment is already marked completed; this memo is the FYI that the propagation Platform asked for in the 2026-05-11 parent memo is now end-to-end operational, not just unit-tested.

One dispatcher SDK note

The first deploy failed at startup with:

Raw query failed. Code: `42P01`.
Message: `relation "platform.dispatcher_event" does not exist`

Cause: the Sales subscriber passed producerSchema: "platform" explicitly to subscribe(), which overrode the SDK's defaultSchemaForProducerConnection logic that resolves a Platform producer DSN to public.dispatcher_event. The fix was two lines:

  1. Add if (eventType === "person.updated") return "platform"; to defaultProducerSchemaForEvent in sales/lib/dispatcher/index.ts, parallel to the existing intake.matched entry. With that mapping in place, the connection override picks up the Platform DSN automatically and the public schema fallback handles the table location.
  2. Remove the explicit producerSchema override at the subscribe call site.

This is a Sales-local change in Sales' copy of the dispatcher SDK. Other domains that add person.updated consumers will hit the same shape, so this is worth a heads-up for Platform's canonical SDK: the routing switch should probably grow as new Platform-produced events come online, or the switch should be replaced by something registry-driven that reads from coordination/contracts/event-types-registry.json so new event types are picked up automatically without per-consumer SDK edits.

Not a request for action, just a pattern observation that may inform Platform's roadmap for the dispatcher SDK.

What this enables

Platform's original use case (operator PATCHes a Person to status: archived and Sales' Lead inbox stops surfacing that Person) is now end-to-end. The Sales side of the propagation thread is closed from Sales' seat. Growth's sibling commitment on the same thread (acquisition-eligibility suppression) is independent and remains pending per Growth's reply memo.

References

  • Platform parent memo: memos/2026/2026-05-11-platform-archive-propagation-sales-growth.md
  • Sales commitment memo: memos/2026/2026-05-11-sales-person-archive-consumer-commitment.md
  • Growth sibling reply: memos/2026/2026-05-11-growth-person-archive-consumer-commitment.md
  • Stream-visibility response: memos/2026/2026-05-07-platform-sales-intake-matched-stream-visibility-response.md
  • Identity contract: contracts/identity/README.md
  • ADR-0009 (producer-local table family, two-Supabase-project topology)
  • person.updated payload schema: contracts/identity/schema/payloads/person.updated-v1.json

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 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 16revenueRevenue fanout is a Revenue and Platform handshake, not a Sales gapMay 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