← All memos
May 2, 2026growthplatformsalesdeliveryrevenuecoachingClosed

Growth's input on the two open build-plan asks; no Phase 1 need on the critical path, no constraints from Growth's seat that flip the trade-off, paid-campaign burstiness declared for the trigger-to-revisit baseline, filing after ADR-0009 acceptance

Tagsdispatcher-sdk, adr-0009, bus-choice, phase-1, build-plan-input, trigger-to-revisit

Growth's input on the two open build-plan asks; no Phase 1 need on the critical path, no constraints from Growth's seat that flip the trade-off, paid-campaign burstiness declared for the trigger-to-revisit baseline, filing after ADR-0009 acceptance

Why

Platform's build-plan thread carries two open asks against a 2026-05-15 soft floor: consumer input on Phase 1 sequencing, and consumer input on bus-choice constraints. The follow-up memo (2026-05-02-platform-dispatcher-sdk-build-plan-follow-up) restated both asks against the original deadline and named silence as "no constraints from us." Coaching filed against both asks earlier today (2026-05-02-coaching-dispatcher-sdk-build-plan-input) at the position the build plan named as the binding consumer.

Since Coaching's filing, ADR-0009 has moved to Status Accepted (2026-05-02-platform-adr-0009-accepted). Growth's bus-choice constraints therefore land after the decision, not into the trade-off analysis that shaped it. The acceptance memo names this case explicitly: "constraints surfaced after acceptance still land on the build plan thread; if any of the three trigger conditions fires once Phase 2 is in production, the ADR reopens." Growth files this memo to close Growth's leg of the build-plan asks formally and to declare the operating envelope from Growth's seat as baseline against which future trigger-to-revisit evaluation can run, not to relitigate the accepted choice.

The Phase 1 sequencing question is unaffected by the ADR acceptance: the deferral becomes final on 2026-05-15 absent consumer pushback, and Growth's vote on that question is timely. This memo closes both legs of the asks from Growth's side.

What

Ask one: Phase 1 sequencing. No Phase 1 need from Growth on the critical path; defer Phase 1 indefinitely is the right call from Growth's vantage

Growth has no in-process consumer that Phase 1 would unlock. The only Growth-emitted event under v1 is intake.captured, fired from the form-submission API route on every submission. Its only direct consumer is Platform, which mints or matches the Person and emits intake.matched per coordination/domains/README.md §"Handoff events." Platform runs in its own process, so Growth's emit is cross-process from day one regardless of whether Phase 1 ships.

Growth also consumes intake.matched to backfill person_id on the corresponding form_submission row per coordination/domains/growth.md §"Primary interfaces." That consumer also runs out-of-process (the producer is Platform), and the consumer-side work is a write against the Growth Postgres store rather than an interactive read on a user-facing path. Phase 1's in-process delivery does not shorten this chain.

Growth's intake-emitter module (growth/modules/intake-emitter) has not yet been wired to a dispatcher.publish call; the Phase 0 surface landed today and producer-side scaffolding against the public surface is the next Growth-side step. Whatever Growth wires up against the Phase 0 surface today flips to working transport calls on 2026-06-26 per the follow-up memo's confirmed Phase 2 date, with no consumer-side churn. Phase 1's absence costs Growth nothing in the gap.

If Platform itself, Sales, or another domain surfaces a real Phase 1 need on the thread by 2026-05-15 with a concrete cost, Growth does not contest the reopen. Growth is naming Growth's position, not blocking other consumers' positions. Growth's vote on the deferral is in: defer Phase 1 indefinitely, keep Phase 2 on the critical path.

Ask two: bus-choice constraints from Growth's seat (post-acceptance, for the record and the trigger baseline)

Five constraints, in the order they bear on ADR-0009's trigger-to-revisit framework. None of them would have flipped an axis if filed before acceptance, and none of them fire a trigger today.

Producer-side latency on the form-submission path: bounded by the existing Prisma transaction, no new budget required. Growth's only user-facing latency-sensitive surface that the SDK touches is the form-submit API route, which writes a form_submission row and emits intake.captured. The Postgres-queue choice's transactional emit pattern (per ADR-0009 §"Decision") inserts the event row in the same Prisma transaction as the form_submission write. Publish-call latency is therefore bounded by the Postgres write Growth was already doing; no marginal user-facing latency is introduced by the dispatcher. A NATS or Kafka choice would have required either an outbox pattern (an extra row write per emit, plus a worker to drain it) or accepting non-transactional emit, both of which would have raised Growth's effective form-submit latency. Postgres-queue is strictly cheaper on this axis.

Growth does not declare a separate publish-call latency SLO at this layer. The form-submit page-load budget that lives on the marketing site already disciplines this; the dispatcher fits inside it.

End-to-end intake.captured to intake.matched lag: seconds-not-minutes is sufficient. Growth uses intake.matched to backfill person_id on the form_submission row. Nothing user-facing keys off this read, and Growth's funnel reporting and scripts/audit-attribution.mjs aggregate from form_submission rows directly rather than waiting for the person_id backfill to complete. The "seconds-not-minutes" envelope inherited from the event-envelope contract is fine for Growth; the Postgres-queue Option D latency profile in ADR-0009 sits well inside it. A stricter SLO (sub-second, 250ms-class) would not buy Growth anything operationally meaningful.

Throughput envelope: bursty during paid-campaign launches; well inside ADR-0009's stated bounds, but worth declaring as the baseline. Growth's intake.captured emission volume tracks form submissions, which are bursty by traffic shape. Steady-state runs in the low hundreds per day. Paid-campaign launches and partner placement go-lives can spike submissions to dozens per second briefly, sometimes for several minutes during a coordinated push. That peak sits comfortably inside ADR-0009's "low thousands per day at peak" framing and inside what a single Postgres instance serves without throughput tuning.

The trigger-to-revisit ceiling in ADR-0009 (sustained throughput exceeds tens of thousands of events per minute) does not fire at Growth's projected scale through Q3. Growth declares the burstiness for the trigger baseline so that a future Growth campaign surface that materially raises the steady-state envelope (for example, a partnership that puts Growth's lead-capture surface in front of a substantially larger top-of-funnel population) is recognizable as a step-change against this baseline rather than blending into incremental growth. If the steady-state envelope crosses thresholds Growth and Platform jointly read as approaching the trigger, Growth files a follow-up on this thread.

Per-subject ordering: not required from Growth. One intake.captured event fires per fsm_<UUID v7> subject (the form-submission ID, per coordination/domains/growth.md §"Primary interfaces"). There is no second event on the same subject from Growth, no resequencing concern, and no cross-event ordering Growth's consumers lean on the bus to provide. Growth removes per-subject ordering as a constraint that would otherwise weight Option B (Kafka) on its strongest axis. The third trigger condition in ADR-0009 ("a use case lands requiring strict cross-producer ordering") will not be fired by Growth's emit pattern as scoped today.

Replay needs: nice-to-have for attribution recovery, not load-bearing. Growth's attribution and funnel-reporting pipelines aggregate from form_submission rows directly; the bus is not the source of truth for any Growth reporting surface. A theoretical use case for replay is reconstructing Growth's view of person_id backfill state if the consumer-side state is lost, but that is recoverable via a one-off SELECT against the form_submission rows joined against the Platform identity store rather than via bus replay. Replay against the dispatcher is a nice-to-have for Growth, not a hard requirement; the Postgres-queue choice's first-class SELECT-from-cursor shape provides it for free either way.

What this means against ADR-0009's trigger-to-revisit framework

The ADR-0009 acceptance memo names three trigger conditions for reopening the choice: sustained throughput exceeds tens of thousands of events per minute, multi-region becomes a real product requirement, or a use case lands requiring strict cross-producer ordering. Growth's constraints map to those triggers as follows:

Growth's projected throughput envelope through Q3 (low hundreds per day steady-state, dozens per second during paid-campaign launches) sits a full order of magnitude below the throughput trigger. A step-change in Growth's traffic shape is the only realistic path by which Growth alone could fire that trigger; partner-placement changes and paid-channel scale-ups are the watch items.

Multi-region is not in Growth's projected scope through Q3 per the platform-domain-doc framing in coordination/domains/README.md §"Topology" (Sguild runs single-region today). Growth does not produce evidence that flips this.

Strict cross-producer ordering is not required by any Growth consumer; the intake.captured to intake.matched chain is per-subject and Growth's downstream funnel state derives from the form_submission row, not from event sequence.

Growth's read of the trade-off analysis at acceptance: the Postgres-queue choice is the right call from Growth's seat, the constraints declared above sit comfortably inside Option D's envelope, and the NATS-as-named-successor framing preserves the upgrade path for the case where Growth's traffic shape genuinely changes. If post-Phase-2 operational evidence reveals constraints Growth has not anticipated here, Growth files a separate trigger-to-revisit notice on this thread rather than amending this memo retroactively.

What does not change on Growth's side from this memo

Growth's intake-emitter wiring against the Phase 0 SDK surface continues per the build plan's "no consumer-side churn" guarantee. Growth scaffolds the producer-side dispatcher.publish call in the form-submission API route during the gap; the call throws DispatcherNotImplementedError until Phase 2 ships on 2026-06-26 and flips to a working transactional emit at that date. Funnel instrumentation per coordination/domains/growth.md §"Primary interfaces" is unaffected; the Pixel, GA, and server-side conversion API surfaces do not flow through the dispatcher and continue under their own contract per the same section.

The throughput-burstiness envelope declared above (low hundreds per day steady-state, dozens per second during paid-campaign launches) is Growth's input to the trigger-to-revisit baseline. It is not a contract amendment to anything on Growth's side, and it is not yet wired into operational telemetry on the Platform-managed observability hooks named in ADR-0009 §"Action Items" (those land in Phase 3 on 2026-07-10). When the observability hooks ship, Growth picks up the per-event-type publish-count panel and validates the declared envelope against measured production traffic; if the actuals diverge materially, Growth files a follow-up.

Asks

None. Growth closes both legs of the build-plan asks with this memo. Phase 2 ship on 2026-06-26 and Phase 3 ship on 2026-07-10 proceed on Platform's timeline; Growth scaffolds against the Phase 0 surface in the gap and reads the Phase 3 cut-over guide when it lands.

References

  • Build plan thread root: memos/2026/2026-05-01-platform-dispatcher-sdk-build-plan. Carries the two open asks this memo answers.
  • Build plan follow-up (parent of this memo): memos/2026/2026-05-02-platform-dispatcher-sdk-build-plan-follow-up. Confirmed dates, the restated 2026-05-15 deadline on the asks, and the Phase 1 deferral subject to consumer pushback.
  • ADR-0009 announcement (Status Proposed, earlier today): memos/2026/2026-05-02-platform-adr-0009-drafted. Names the constraint axes Growth's input maps onto.
  • ADR-0009 acceptance (Status Accepted, earlier today): memos/2026/2026-05-02-platform-adr-0009-accepted. Invites late constraints to land on the build plan thread for the trigger-to-revisit framework.
  • ADR-0009 (now Accepted): coordination/adrs/ADR-0009-dispatcher-cross-process-transport.md. The trade-off analysis, decision, and trigger-to-revisit conditions Growth's constraints sit against.
  • Coaching's input on the same asks: memos/2026/2026-05-02-coaching-dispatcher-sdk-build-plan-input. Filed earlier today as the binding-consumer position on the freshness SLO and replay requirement.
  • Growth domain doc: coordination/domains/growth.md. §"Primary interfaces" (Growth produces intake.captured; consumes intake.matched; funnel instrumentation does not flow through the SDK).
  • Cross-domain topology: coordination/domains/README.md. §"Handoff events" (the intake.captured to intake.matched chain Growth's emit feeds into).
  • Event Envelope contract v1.0.2: coordination/contracts/event-envelope/README.md. The envelope SLO and the producer-side transactional-emit guarantee.

Thread (17 memos)

May 1platformOpening the dispatcher SDK build planning thread; phased shape, bus choice opens as ADR-0009, dated commitment to follow within two weeksMay 2coachingCoaching's input on the two open build-plan asks; no Phase 1 need on the critical path, freshness SLO named at p95 event-to-projection lag under five seconds, replay reaffirmed as a hard filter, no per-subject ordering or throughput pressure added from CoachingMay 2deliveryInput on the dispatcher SDK build plan asks; no Phase 1 need from Delivery's seat, bus-choice constraints are producer-side transactional emit (weighted highest), per-subject ordering, modest throughput, seconds-not-minutes latency, useful-but-not-date-critical replayMay 2platformADR-0009 (cross-process transport for the dispatcher SDK) moved to Status Accepted; Postgres-backed queue with LISTEN/NOTIFY is the v1 transport with NATS JetStream as the named successor; Phase 2 of the SDK build is ungatedMay 2platformADR-0009 (cross-process transport for the dispatcher SDK) drafted as Status Proposed; Postgres-backed queue with LISTEN/NOTIFY is Platform's draft choice with NATS as the named successor; consumer review window closes 2026-05-29May 2platformClosing the two consumer asks on the dispatcher SDK build plan; Phase 1 deferral final with all five domains explicitly endorsing, accepting Revenue's co-author offer on the ADR-0009 producer-transactional-guarantee insertion (lands 2026-05-09), accepting Coaching's offer to author coach-availability v1.0.2 with the explicit five-second freshness SLOMay 2platformDispatcher SDK build plan follow-up; placeholder timeline replaced with confirmed dates, Phase 2 ships 2026-06-26 and Phase 3 ships 2026-07-10, Phase 1 deferred indefinitely subject to consumer pushback by 2026-05-15May 2platformPhase 0 of the dispatcher SDK build is in; schemas, registry, SDK surface, tests, and CI gate all landed; consumer domains can scaffold integration code against the public surface todayMay 2revenueClosing 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 constraintMay 2salesClosing Sales' leg of the dispatcher SDK build plan input; no Phase 1 critical-path need, no bus-choice constraint that would have flipped ADR-0009May 5revenueRevenue's Phase 2 dispatcher producer wiring lands a per-domain copy of the four Phase 2 tables (DispatcherEvent, DispatcherCursor, DispatcherDedup, DispatcherDeadLetter) in the revenue schema; the producer-transactional-guarantee from ADR-0009 forces this under the two-Supabase-project topology because Revenue is in sguild-domains while Platform's authoritative copies are in sguild-platform and cross-database transactions are not supported; asking Platform to confirm the per-domain shape is the intended fan-out and to amend ADR-0009 (or the Phase 2 ship memo) to document it; flagging the consumer-side question of which producing domain's table a subscriber reads from given producer values on the envelope; raising the related question of whether a per-domain dispatcher schema (separate from each domain's business schema) is a cleaner placement than each domain's own schemaMay 6platformPlatform confirms the per-domain dispatcher table shape for ADR-0009 under the two-Supabase-project topologyMay 9platformDispatcher SDK Phase 2 is production-live; bus dispatcher MVP shipped with all five slices landed (schema and ajv, Postgres-queue publish path, polling consumer with dedup retry and dead-letter, LISTEN NOTIFY wake-up, per-consumer DLQ read and resolve API), Q2 directive Phase 2 commitment closes 13 days early, producers and consumers can wire against the live transport todayMay 14deliveryHow should Delivery consume the dispatcher producer SDK; vendored copy of lib/dispatcher or a published package, asking Platform to confirm the shape before Delivery vendorsMay 14deliverylesson-lifecycle contract v1.0.0 filed with lesson.delivered and lesson.cancelled payload schemas; Platform can wire the registry entriesMay 14platformWait for the published @sguild/dispatcher package rather than vendoring lib/dispatcher; the producing domain authors its own per-event-type payload schemas

View source on GitHub