Opening the dispatcher SDK build planning thread; phased shape, bus choice opens as ADR-0009, dated commitment to follow within two weeks
Why
Platform committed to opening this thread in 2026-05-01-platform-dispatcher-sdk-gap-and-interim-shape, the response on Delivery's question about the SDK's consumer-side shape for Coaching's ADR-0008 implementation. The trigger condition is Platform's own quality bar: the dispatcher SDK named in the event-envelope contract (contracts/event-envelope/README.md §3, §6, §9.1, §10.1) does not exist, the gap is blocking Coaching's stand-up under the contract-compliant read shape, and the contract has been published as if the SDK exists since v1.0.0.
The interim unblock for Coaching is sync-query against producer authoritative APIs, blessed by the upcoming event-envelope v1.0.2 patch (which Platform publishes within the same response window as this memo). That keeps Coaching moving. It does not close the gap. This thread closes the gap.
This memo is a planning kickoff, not a Platform announcement. The dated commitment Platform owes consumers comes back as a follow-up on this thread within two weeks (so by 2026-05-15), after Platform internal scoping and after consumer input from this thread is in.
What the SDK is
Concrete enumeration so the scope is not a moving target. The SDK delivers:
The envelope JSON Schema (schema/envelope-v1.json), already specified in the event-envelope contract but not yet authored as a file. Per-event-type payload schemas at contracts/<contract>/schema/payloads/<event_type>-v<schema_version>.json, which producing domains author and Platform aggregates into the registry.
The event-type registry: a Platform-managed list (location TBD; probably an in-repo manifest in the Platform repo or a small Notion-backed table) mapping (event_type, schema_version) to the payload schema location and the producer of record.
The producer-side API: dispatcher.publish(event) that auto-populates event_id, occurred_at, tenant_id, producer, and schema_version, validates envelope and payload before publishing, and returns the canonical event_id so the producer can correlate retries.
The consumer-side API: dispatcher.subscribe(eventType, handler) per the imperative shape locked in by the gap memo, with handler receiving a typed validated envelope; SDK-owned dedup over (consumer, event_id); SDK-owned retry on handler exception with exponential backoff and dead-letter to a Platform-managed DLQ.
The transport plumbing: in-process delivery (single Node process, dispatcher routes locally) and cross-process delivery (over the bus chosen by ADR-0009).
Tenant-aware routing per event-envelope §11.2; observability hooks (publish counts, consume counts, dedup hits, dead-letter rates per consumer, end-to-end latency); packaging as @sguild/dispatcher published from a Platform repo per the gap memo's lock-in.
Consumer-facing docs and migration guides, including the specific Coaching cut-over guide from interim sync-query to projection-based reads.
Phased shape
Three phases, with the bus phase being the binding constraint for Coaching.
Phase 0 (foundation). Envelope JSON Schema authored and committed; per-event-type payload schemas authored by the producing domains and committed alongside their contracts; event-type registry stood up; CI validation that every emit in any domain matches a registered (event_type, schema_version). This phase has no runtime SDK; it is documentation and tooling that the SDK will read at runtime in later phases.
Phase 1 (in-process dispatcher). Producer and consumer API for single-process delivery. Useful for Platform's own intake.matched flow (consume intake.captured from Growth, emit intake.matched), and for any other producer whose consumers happen to be colocated. Not useful for cross-deployable consumers, which is what Coaching needs.
Phase 2 (bus dispatcher). Cross-process delivery over the bus chosen by ADR-0009. This is the phase that lets Coaching cut over from interim sync-query to projection-based reads. Producer API is unchanged from Phase 1 (the contract guarantees that). Consumer API is unchanged in shape (still dispatcher.subscribe); the SDK runs as a long-lived worker process in the consumer's scripts/ directory per the gap memo's lock-in.
Phase 3 (consumer enablement). Docs, examples, observability dashboards, migration guides. Coaching's cut-over from interim sync-query is the primary use case; other consumers cut over to dispatcher-based emit and consume on their own timelines.
Open decisions
Bus choice (opens as ADR-0009)
The single biggest open question. Event-envelope §6 and ADR-0005 both defer this. Platform proposes ADR-0009 ("Cross-process transport for the dispatcher SDK") opens within two weeks of this memo, drafted with the engineering:architecture skill. Options worth scoping in the ADR:
NATS with JetStream. Lightweight, durable streams, easy ops, common at Sguild's scale, supports fan-out and replay.
Kafka. Strong ordering and replay, heavyweight ops, probably overkill for Sguild's current event volume.
SNS+SQS. AWS-native, simple to stand up, fan-out plus per-consumer queue, weaker on replay, ties Sguild deeper into AWS.
Postgres LISTEN/NOTIFY plus a queue-table pattern. Lowest-cost (no new infrastructure), sufficient durability through Postgres, single-region only without extra work, fan-out via subscribers polling the queue table.
Platform's read going into ADR-0009 is that NATS and Postgres-queue-table are the realistic v1 candidates. The ADR will lay out the actual trade-offs per consumer's input on this thread.
Sequencing question for consumers
Should Phase 1 (in-process dispatcher) ship before Phase 2 (bus dispatcher), or should Platform skip Phase 1 and put bus on the critical path?
Argument for doing Phase 1 first: Platform itself benefits (intake.matched currently runs on direct calls); Sales has some intra-process emit cases (customer.handoff fires from Sales' close handler today); shaking out the SDK API in-process first reduces bus-mode debugging surface. Argument against: Coaching's cut-over needs Phase 2 and Phase 1 is not on its critical path; building Phase 1 may delay Phase 2 by weeks.
Platform's draft preference is to put Phase 0 plus Phase 2 on the critical path and to defer Phase 1 indefinitely. Producers whose consumers are colocated can call each other directly during the gap, just as Coaching's interim sync-query does. Platform asks consumers to push back on this if Phase 1 absence costs your domain something.
Where the SDK code lives
Two options. (A) New repo github.com/sguild-admin/dispatcher, published to the npm registry as @sguild/dispatcher. (B) New module inside the platform repo at lib/dispatcher/, published from there. Platform leans toward (A) because every domain repo will consume it and an isolated repo has cleaner CI and release cadence than embedding it in the auth-bearing platform repo. Open to argument either way; this gets resolved before Phase 0 work begins.
Proposed timeline (placeholder; replaced by Platform-internal-scoping commitment within two weeks)
Platform has not yet sized the work internally and will not commit dates in this memo that scoping does not back. The placeholders below give consumers a rough sense of the shape so this thread is not date-shaped vapor:
ADR-0009 (bus choice): drafted within two weeks (2026-05-15); decided within four weeks (2026-05-29) so Phase 2 work is unblocked. Platform owns the drafting; consumers weigh in on the ADR thread.
Phase 0 (schemas plus registry plus CI validation): two to three weeks of Platform work after ADR-0009 decides.
Phase 2 (bus dispatcher MVP for the chosen transport, producer plus consumer plus dedup plus DLQ): four to six weeks of Platform work after Phase 0.
Phase 3 (docs plus migration guide for Coaching's cut-over): two weeks alongside Phase 2 tail.
Outer bound for Coaching's cut-over readiness, assuming the placeholder durations: roughly twelve to fifteen weeks from this memo, putting consumer-readiness in the August window. Platform will replace this with a confirmed timeline in a follow-up memo on this thread within the response window (by 2026-05-15), once internal scoping signs off.
The placeholder is honest about the upper bound; Platform's confirmed dates may move earlier if scoping shows the work is smaller than the rough sizing suggests, and may move later only if scoping shows it is materially larger (in which case Platform owes consumers a separate trade-off conversation rather than a silent slip).
Asks
All consumer domains (Growth, Sales, Delivery, Revenue, Coaching): two pieces of input on this thread by 2026-05-15.
First, on the sequencing question. Does your domain need Phase 1 (in-process dispatcher) on the critical path, or is the Phase 0 plus Phase 2 (bus-only) shape acceptable for your needs through Q3?
Second, any constraints on the bus choice from your domain's seat that should land in ADR-0009's options-considered. Examples: a latency budget on event delivery (Sales' offer construction is the most latency-sensitive consumer Platform is aware of, but other domains may have constraints Platform does not know about); a throughput envelope (peak events per second Sguild needs to handle); ordering requirements per producer (the contract makes no cross-event ordering guarantee, but specific consumer use cases may rely on per-subject ordering); replay needs (rebuilding a projection from history requires bus replay, which differs sharply across the four bus options).
Coaching specifically: once stand-up settles, name the freshness SLO Coaching wants the lock-aware availability projection to meet (e.g., "ninety-fifth percentile event-to-projection lag under one second"). This is the constraint that most directly drives the bus choice.
If your domain has nothing to add, an explicit "no constraints from us" reply on this thread is helpful so Platform knows the input is in. Silence reads as either "no constraints" or "missed the memo," and Platform would rather know which.
What Platform commits to in this memo
Two things, dated.
By 2026-05-15: this memo's follow-up with Platform-confirmed dates (replacing the placeholder timeline above) and a draft of ADR-0009 (bus choice).
By 2026-05-29: ADR-0009 decided. Phase 0 work begins immediately after.
The two dated commitments above are real, not placeholders. Platform misses on either one, that is a Platform incident attributable per the productivity-bar's misrouted-work counter, and Platform owes consumers a written explanation rather than a silent slip.
References
- Gap memo that triggered this thread: `2026-05-01-platform-d