Coaching'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-20
Why
Per the topology B decision in 2026-05-12-platform-cross-db-consumer-dsn-upstream, domain inboxes are the stateless HTTP receivers Platform's fanout worker POSTs to. Coaching's inbox is now wired, schema-aligned, and locally smoke-verified end-to-end, and is ready for Platform's fanout worker to start routing credit.* events to it. Filing this on the topology thread so Platform's registry update and secret provisioning land as one coordinated step rather than a back-and-forth.
What is live
The inbox route is at <coaching-host>/coaching/api/dispatcher/inbox. The /coaching segment is the basePath set in next.config.mjs and is not configurable per-event. The production deploy URL for Coaching becomes the canonical consumer endpoint for these event types.
Auth is HMAC-SHA256 over the raw request body, hex-encoded, sent in the x-sguild-signature header. Coaching accepts an optional sha256= prefix per the verify code in lib/dispatcher/inbox.ts. The signing helper is lib/dispatcher/signature.ts, vendored from Platform per the 2026-05-12 topology decision. Re-vendor if Platform amends.
Consumer secret is read from DISPATCHER_INBOX_SECRET on Coaching's side. Platform's fanout worker should provision DISPATCHER_CONSUMER_SECRET_COACHING to the same value, matching the per-consumer secret pattern Platform established for Sales and Growth.
Event types Coaching consumes: credit.reserved, credit.locked, credit.released, credit.forfeited per coach-availability v1.0.1 §4.3.1 and the §12 commitments Coaching adopted in 2026-05-02-coaching-platform-day-one-subscriber-ack. Coaching does NOT consume credit.funded (Revenue-internal reconciliation per §4.3.1) or credit.consumed (terminal-past; the projection ages out past windows on a clock). The inbox route's switch statement returns 200 with no-op handling for any event_type outside the four above, matching the topology decision's stateless-receiver shape.
The handler downstream of dedup writes the projection row in coaching.projection_state keyed on (organization_id, coach_id, window_start). The state transition follows the subtraction rule in coach-availability v1.0.1 §4.3.2: credit.reserved writes reserved, credit.locked writes locked, credit.released and credit.forfeited write free. Idempotency on the projection-state grain backstops missed-dedup scenarios per the §4.3.4 convergence rule.
What the smoke verified
Local smoke-tested 2026-05-13 against a synthetic credit.reserved envelope. Three outcomes confirmed on three POSTs to the inbox:
First POST with valid signature: HTTP 200 with empty body. A row landed in coaching.projection_state with state=reserved, the synthetic reservation_id, and the synthetic window.
Second POST with the same event_id and a valid signature: HTTP 200 with empty body, via the dispatcher_inbox_dedup short-circuit. No second projection-state row was written. The dedup table caught the retry on event_id uniqueness exactly as the topology decision specified.
Third POST with an invalid signature: HTTP 401 with body {"error":"invalid HMAC signature"}. The HMAC verification rejects malformed signatures before any state is touched.
The smoke script is scripts/smoke-inbox.mjs in the Coaching repo if Platform wants to run the same shape from the fanout worker's side before flipping the registry.
Asks
Two, dated.
Add Coaching to coordination/contracts/event-types-registry.json as a consumer for credit.reserved, credit.locked, credit.released, and credit.forfeited by 2026-05-20. Each entry points at Coaching's inbox URL. If Platform's registry format expects a single URL per consumer rather than one entry per event type, name the shape on this thread and Coaching folds in.
Provision DISPATCHER_CONSUMER_SECRET_COACHING on Platform's fanout worker by 2026-05-20. Coaching can share a value via a secure side-channel; Platform names the channel it prefers. Rotation pattern follows whatever Platform established for Sales and Growth; if separate-per-consumer rotation is the pattern, Coaching folds in.
If Platform's fanout worker needs Coaching to participate in a one-off webhook POST from Render's IP space before flipping the registry on (a connectivity test from the actual fanout host), name the test shape on this thread and Coaching coordinates against the production deploy.
What does not change
The contract surfaces stand. coach-availability v1.0.1 stays as written; the §4.3 lock-aware projection mechanics are what the inbox writes against. ADR-0009's Postgres-queue transport is the producer side Platform's fanout polls. ADR-0005's event envelope is what the inbox verifies and routes. The §12 consumer responsibilities Coaching adopted in 2026-05-02-coaching-platform-day-one-subscriber-ack continue to bind in full from this filing onward.
The Coaching domain doc's identity rule and comms-routing rule are unaffected by inbox standup; this memo only wires the event-flow side of the cross-domain surface.
References
- Topology decision (parent thread root for this filing):
memos/2026/2026-05-12-platform-cross-db-consumer-dsn-upstream - Coaching's day-one subscriber ack (carries the §12 consumer responsibilities now live):
memos/2026/2026-05-02-coaching-platform-day-one-subscriber-ack - Cutover ship memo (records the engineering completion the inbox sits atop):
memos/2026/2026-05-15-coaching-projection-cutover-shipped - Initiative pair (the in-flight migration this filing operationalizes):
memos/2026/2026-05-07-coaching-migration-and-scheduling-initiative-pair - Coach Availability contract v1.0.1 (the §4.3 mechanics the inbox writes against):
coordination/contracts/coach-availability/README.md - Credit Reservation Lock contract (the producer-side surface Platform's fanout polls):
coordination/contracts/credit-reservation-lock/README.md - ADR-0005 (event envelope; the shape the inbox verifies):
coordination/adrs/ADR-0005-event-envelope.md - ADR-0009 (Postgres-queue with LISTEN/NOTIFY; the transport between producer dispatch and fanout poll):
coordination/adrs/ADR-0009-dispatcher-cross-process-transport.md - Coaching repo inbox route:
app/api/dispatcher/inbox/route.tsingithub.com/sguild-admin/coaching - Coaching repo signing helpers:
lib/dispatcher/inbox.tsandlib/dispatcher/signature.ts(vendored from Platform per the 2026-05-12 topology decision) - Coaching repo smoke harness:
scripts/smoke-inbox.mjs(HMAC-signed POST that exercises route, dedup, and projection write end-to-end)