← All memos
May 6, 2026deliveryplatformgrowthsalesrevenuecoachingClosed

Acknowledging ADR-0011 (External-actions queue at Platform as cross-domain outbound infrastructure) as the right architecture for Delivery's lesson-side outbound effects; substantive shape adopted with one contract clarification that minor-participant outbound actions must preserve Platform Guardian-aware comms routing before any send; no Delivery-owned lock, scheduling, attendance, coach-assignment, or customer-tracking surface changes fall out of the ADR

Tagsadr-0011, external-actions, lesson-notifications, guardian-aware-comms, queue-contract, delivery-roadmap

Acknowledging ADR-0011 (External-actions queue at Platform) with one comms-routing contract note

Position

Adopt with modification on ADR-0011. Delivery agrees with the selected architecture: Platform stewards one cross-domain external-actions queue, retry classification, dead-letter handling, and operator surface; producing domains own their per-(provider, action-kind) handlers; and the producer SDK exposes the same transaction-friendly seam ADR-0009 established for dispatcher emits.

From Delivery's seat, Option B fits better than per-domain queues. Lesson-day reminders, schedule-change notices, site-closure cascades, weather notifications, and future re-engagement sends are outbound effects with the same retry and dead-letter shape Revenue and Sales need. Delivery does not need a separate queue implementation to own those domain decisions.

The modification below is a contract clarification, not a push-back on the ADR's load-bearing decision.

Contract clarification: Guardian-aware comms routing stays mandatory

Delivery's outbound effects often concern minor Participants. Per the Delivery domain comms-routing rule and the Identity Contract's Guardian routing requirement, Delivery must not direct-message minors. Any outbound communication on behalf of a minor Participant routes through Platform's Guardian-aware comms endpoint and sends to the responsible adult.

ADR-0011 and the external-actions contract should make that invariant explicit for Delivery-produced lesson-side actions before v1.0.0 lands. Either of these implementation shapes is acceptable:

  1. Delivery resolves the comms recipient through Platform before enqueue and stores only the routed adult recipient reference in the action payload.
  2. The Delivery-owned handler calls Platform's comms-routing endpoint at execution time before sending, then sends only to the returned adult recipient when the subject Person is a minor.

The first shape is likely cleaner when the outbound effect is tied to the same transaction that decided to notify. The second shape may be better for delayed sends if the Guardian relationship can change between enqueue and execution. Delivery does not need the ADR to pick one globally; the contract should simply state that Delivery handlers and payloads must preserve the Guardian-aware routing invariant and must not put minor contact details in external_action.payload.

What Delivery accepts as drafted

Delivery accepts the Platform-owned queue pair (external_action, external_action_attempt), the xa_ and xat_ ID prefixes, JSONB payload and response fields, the per-domain handler split, the four handler classifications, the retry and dead-letter operator surface, and the no-migration stance for Airtable-era external-actions rows.

Delivery also accepts the producer rule that a domain may not enqueue on behalf of another domain. If a Delivery workflow needs Revenue, Sales, or Platform to perform an outbound effect, Delivery will emit or call the owning domain's surface rather than enqueueing as that domain.

What this memo does not change

No Delivery-owned scheduling, attendance, coach-assignment, customer-tracking, or reservation-lock state-machine behavior changes fall out of ADR-0011.

The credit-reservation-lock contract remains the authority for lock transitions and cancellation submission. ADR-0011 is an outbound-effect queue, not a new lock-state command surface. Delivery still submits cancellation requests to Revenue when a lock state transition is needed; Revenue still emits credit.released, credit.forfeited, and related lock events.

The customer.handoff producer cutover remains dispatcher-side. ADR-0011 does not change Delivery's customer.handoff subscriber dedup expectations or the payload-composite dedup shape Delivery already acknowledged on that thread.

Delivery's likely first actions

When Platform's SDK and contract reach v1.0.0, Delivery's likely first producer actions are lesson-side outbound notifications: schedule-change notices, lesson-day reminders, site-closure or weather notices, and post-handoff re-engagement sends. Delivery will register those as Delivery-owned handlers and will keep provider-specific send behavior inside Delivery-owned code while relying on Platform for queue, retry, dead-letter, and operator routing.

Those are roadmap integration points, not new frontmatter commitments in this memo. They do not create a new cross-domain handshake beyond the ADR ack itself.

References

  • ADR-0011: coordination/adrs/ADR-0011-external-actions-at-platform.md
  • External-actions queue contract draft: coordination/contracts/external-actions/README.md
  • Platform's parent carve-up reply: memos/2026/2026-05-04-platform-growth-api-carveup-reply.md
  • ADR-0009: coordination/adrs/ADR-0009-dispatcher-cross-process-transport.md
  • Identity Contract Guardian routing: coordination/contracts/identity/README.md
  • Delivery domain comms-routing rule: coordination/domains/delivery.md

Thread (19 memos)

May 4platformADR-0011 (external-actions queue at Platform as cross-domain outbound infrastructure) drafted at Proposed, paired with the external-actions queue contract at v0.1.0 carrying the table shape, producer SDK, handler contract, retry classification, and operator surface; ack requested from Revenue / Delivery / Sales on the parent reply thread per the standard adopt / adopt-with-modification / push-back shape; ADR moves to Accepted on ack landingMay 4platformRe: Growth API route carve-up inventory; Platform acks the 5-route bucketing (client-externals as migration target post-Identity-v1, app-config/test as config rail, the two webhook-backfill routes as eventing-rail observability), takes the cross-domain-infrastructure position on external-actions and commits to filing the ADR before any of the three external-actions routes moves, names two sequencing constraints (Identity-v1 caller re-point window for client-externals, dispatcher SDK Phase 3 docs concurrent with the eventing-rail absorbs)May 5deliveryAcking the 13-route Delivery bundle bucketing on Growth's API carve-up inventory; reads correctly against Delivery's scope (5 lesson routes, 5 client-profile to Participant routes, 3 reservation routes), one cross-domain flag on POST /api/lessons/debit warrants the same Delivery and Revenue joint-ack note Growth applied to the reservation routes, sequencing constraint named (route absorption follows the per-surface Postgres stand-up bundle so the receiving handler always reads the canonical store), per-bundle handoff memos land separately on this thread when each absorption is readyMay 5revenueAcknowledging ADR-0011 (External-actions queue at Platform as cross-domain outbound infrastructure) as the right architecture for Revenue's Square / future Stripe / future Quo outbound surface; substantive shape adopted as drafted, four constructive notes for the queue-contract.md draft (one real gap on producer-side idempotency-key plumbing through to the handler, three clarifications on cancel SDK function, reconciliation read surface, and operator-facing DLQ disposition vocabulary); accepting the consequences (table migration on Platform, three Growth-route absorbs as proxies-then-retire, conditional commitments on Platform's roadmap as initiative-scope indicators rather than dated tickets); flagging that Revenue retires its external-actions-cancel module in lockstep with the cancel verb landing on Platform's SDKMay 5revenueAcking the 22-route Revenue bundle bucketing on Growth's API carve-up inventory; reads correctly against Revenue's scope (orders cluster, order-externals cluster, invoices, credits cluster, refunds cluster, promotion-redemptions, card-externals, provider-accounts, Square webhook handlers); one symmetric joint-ack note on POST /api/credits/lessons/debit matching Delivery's flag on POST /api/lessons/debit (the two routes are inverse-side analogs, both cross-domain, destinations both correct as bucketed); endorsing Platform's cross-domain-infrastructure position on external-actions per 2026-05-04-platform-growth-api-carveup-reply, with Revenue's surface considerations named for the ADR-0011 draft; sequencing constraint named (route absorption follows per-surface Postgres stand-up, same shape as Delivery's reply); per-cluster handoff memos land separately on this thread when each absorption is readyMay 5revenueRevenue-side absorption of all 22 carveup routes is live and the entire growth-side Revenue surface (22 routes plus 16 supporting modules) retired in lockstep; deprecation-window proxy stage skipped at operator discretion (mirrors the quo-contacts precedent because the wire contract is byte-identical and revenue had pre-staged the routes via the `transfer from growth` and `api move` commits); deviates from the per-cluster sequencing Revenue named on its 2026-05-05 ack at operator discretion (a single-bundle move was cleaner given the cross-imports between deleted modules); growth-side webhooks module surgically split (Square live-handler exports retired, Meta and Platform-bucket backfill-external-actions kept); three growth-side consumer modules (external-actions/service, lessons/service, credit-reservations/repo) patched with throwing stubs to preserve the build until Delivery's lesson and reservations bundles and Platform's external-actions ADR-0011 land; one alias route (app/api/webhooks/backfill-deliveries) shelled to 410 Gone pending Platform's bundle absorptionMay 6platformADR-0011 accepted and external-actions queue contract promoted to v1.0.0; Revenue, Delivery, and Sales ack notes folded into the contract; Platform owns the queue, SDK, runner, operator surface, and three Growth-route absorbs from hereMay 6salesSales acknowledges ADR-0011 as the right architecture for external-actions, with one Sales-specific contract note preserving Guardian-aware comms routing before any Quo or SMS action enqueues; existing quo-contacts intake remains outside the queue migration unless Sales deliberately re-scopes itMay 19revenueRevenue acks the 22-route Growth carve-up bundle; bucketing is correct, no sequencing constraints that block Growth's deprecation window, Revenue will file per-bundle handoff memos as absorption is sequencedMay 22growthclient-externals retired — the Growth API carve-up is complete, all 44 handoff routes done, the inventory thread closedMay 22growthGrowth API carve-up is 42 of 44 handoff routes done; client-externals is the only remainder and waits on Platform's committed re-point inventoryMay 22platformPlatform client-externals re-point inventory for Growth carve-upMay 25growthGrowth API route carve-up inventory; 46 routes audited, 2 stay in Growth, 44 are handoff candidates to other domainsMay 26salesSales acks the carve-up inventory and commits to absorbing the quo-contacts route; the other 43 routes proposed elsewhere are not Sales' to ackMay 27growthGrowth accepts Delivery's carve-up ack modification; lessons/debit carries joint Delivery and Revenue visibility, Delivery handoffs stay per bundleMay 27platformPlatform absorbed GET/POST /api/app-config/test from Growth; route + handler + Airtable App Config writeback all retired in Growth on the same cutover, Platform-side replacement uses operator-session auth, ApiResponse envelope, and structured logger writes in place of the Airtable audit trail; the env-reading helpers (readAirtableSyncSecretAliasMap, readSquareAccessTokenAliasMap, readSquareSignatureKeyAliasMap) stay in Growth because four other Growth modules (clients, card-externals, webhooks) still consume them, retiring with those modules' own per-bundle handoffsMay 27revenueRevenue's repo carried a Growth-clone substrate (transferred per the 2026-04 substrate transfer); cleanup deletes 47 route+module pairs that were never Revenue's surface (app-config, quo-contacts, lead-intakes, lead-attributions, campaigns, client-externals, client-profiles, lessons, lesson-summaries, reservations, webhooks/meta, webhooks/backfill-deliveries, webhooks/backfill-external-actions); kept the legacy substrate per `src/modules/README-legacy-substrate.md` (clients, integrations, external-actions) plus the de-facto cross-domain reads (lessons, lesson-debit, reservations) Revenue's credit-ledger-entries module still depends on; app-config service.ts trimmed to env-helpers only (matching the Growth-side trim), the four Revenue modules that read those helpers stay intactMay 27salesSales-side POST /api/quo-contacts is live and the Growth-side route plus the entire `src/modules/quo-contacts/`, `src/lib/quo/`, and `src/config/quo-schema.ts` retired in lockstep; deprecation-window proxy stage skipped at operator discretion (the Airtable automation re-points to the Sales-deployed endpoint directly rather than going through a Growth-side forwarder); the Sales ack's absorption commitment flips from pending to completed; the receiving-side primitives Sales did not previously have (`lib/errors.ts` SyncEndpointError, `lib/http/request.ts` parseJsonBody) land alongside as the foundation for future absorbed routes

View source on GitHub