# Enterprise migration: `@suaveplan/sms` - **Change ID**: `enterprise-sms` - **Status**: PROPOSED - **Capability**: `sms` - **Target package**: `@suaveplan/sms` at `0.1.0` - **Target path**: `packages/communication/messaging/sms/` - **Design**: ./design.md - **Spec delta**: ./specs/sms/spec.md - **Source on main**: `/mnt/s/web/suaveplan/packages/server/sms` (`~1389 LOC`) - **Enterprise plan**: `/mnt/s/web/suaveplan/enterprise/sms.md` ## Why `@suaveplan/sms` is the universal SMS core: a provider-agnostic sending pipeline with template rendering, opt-out management, rate limiting, scheduling, retry, and delivery tracking. Providers (Twilio, Vonage, MessageBird, AWS SNS, in-house gateways) plug into the abstract `SmsProvider` contract. The upstream package (`v1.0.2`) is functionally narrow — provider seam, template render, opt-out list, four events — but is missing every enterprise lever the genesis tree commits to: - `BaseError` legacy hierarchy instead of `defineCodes("SMS", [...])`. - No Zod boundary on `SmsMessage`, `SmsTemplate`, `SmsProvider` inputs. - No OpenTelemetry counters or histograms. - No retry/backoff on transient provider failures. - No scheduling (`sendAt`) or batched dispatch. - No rate limiting per provider / per recipient. - No delivery-receipt ingest pipeline; status is fetched on demand. - No `AbortSignal` propagation on send / scheduler / status pulls. - No in-memory test provider; tests use ad-hoc anonymous mocks. - One global event-emitter shape; no typed `EventBus` bridge. - Tests pass with vi-mocks (`@suaveplan/testing` `vi.fn()`) — fine for unit harnesses, but the package never exercises a real provider round-trip end-to-end. This change rewrites the package on genesis at enterprise quality while preserving every upstream export, then layers the SMS-core features (`MessageScheduler`, `RateLimiter`, `DeliveryTracker`, `RetryPolicy`, OTel instruments, Zod boundaries, abort signals, `EventBus` bridge, in-memory test provider). ## Parity Checklist | Capability | Upstream | Migrated | Notes | |---|---|---|---| | `SmsService` send / template render / opt-out | ✅ | ✅ | Preserved verbatim, plus rate-limit + abort + retry on top. | | `SmsProvider` abstract contract | ✅ | ✅ | Preserved (`name`, `send`, `getStatus`). | | `SmsTemplate` (`{{var}}` substitution) | ✅ | ✅ | Preserved + `defaults` map + escape pass. | | `SmsMessage` / `SmsDeliveryReport` / `SmsDeliveryStatus` | ✅ | ✅ | Preserved + Zod schemas. | | `OptOutEntry` registry | ✅ | ✅ | Preserved + bulk import / export. | | `SmsEventMap` (`sms.sent`/`delivered`/`failed`/`opted_out`/`opted_in`) | ✅ | ✅ | Preserved + new `sms.scheduled` / `sms.retry` / `sms.rate_limited`. | | Legacy `EventEmitter` surface (`on/off/emit`) | ✅ | ✅ | Preserved (delegates to typed `EventBus`). | | `SmsError` legacy class + factory methods | ✅ | ✅ | Preserved as back-compat alias over genesis `defineCodes` family. | | `respondProblemJson` Hono helper | ✅ | ✅ | Preserved. | | `registerSmsErrorCodes` / `SMS_ERROR_METADATA` | ✅ | ✅ | Preserved via genesis `ErrorCodes.registerMany`. | | Stable `SMS.*` `defineCodes` registry | 🚫 (legacy `BaseError`) | ⬆️ | Genesis addition. | | Zod boundary on every public input | 🚫 | ⬆️ | Genesis addition. | | OTel counters + histogram | 🚫 | ⬆️ | Genesis addition (4 instruments). | | `MessageScheduler` (sendAt + cancel) | 🚫 | ⬆️ | Genesis addition. | | `RateLimiter` (token bucket per recipient) | 🚫 | ⬆️ | Genesis addition. | | `DeliveryTracker` (receipt ingest + cache) | 🚫 | ⬆️ | Genesis addition. | | `RetryPolicy` (exponential backoff w/ jitter) | 🚫 | ⬆️ | Genesis addition. | | `InMemorySmsProvider` (test provider) | 🚫 | ⬆️ | Genesis addition; replaces ad-hoc test mocks. | | `AbortSignal` propagation | 🚫 | ⬆️ | Genesis addition. | | 100/100/100/100 coverage | 🚫 | ⬆️ | Genesis gate. | ## What A net-new package at `packages/communication/messaging/sms/` at `0.1.0`, universal tier (no Node/Bun-only APIs), Zod-validated boundaries, OTel-instrumented, with the full upstream public surface plus the genesis enterprise additions. This is the SMS **core** — not the providers package. The abstract `SmsProvider` interface ships with an in-memory test provider (`InMemorySmsProvider`) so consumers and integration tests can run without external services. Concrete provider integrations (Twilio, Vonage, AWS SNS) ship in the sibling `@suaveplan/sms-providers` package next wave. ## Impact ### Affected dependents Run `bunx turbo ls --filter=...@suaveplan/sms` to enumerate. No genesis package currently imports `@suaveplan/sms`: - `@suaveplan/sms-providers` — pending migration, will consume this as `workspace:^0.1.0`. [x] no action needed (reason: package not yet on genesis) - `@suaveplan/api-plugin-messaging` — pending migration. [x] no action needed (reason: package not yet on genesis) Foundation deps (`@suaveplan/error`, `@suaveplan/events`, `@suaveplan/types`) are read-only consumers; no change required. ## Out of scope Nothing. Per `feedback_no_deferrals_on_migration.md` and AGENTS §2b, no "phase 2" is permitted. The whole upstream surface plus the enterprise additions land here, feature-complete, with 100/100/100/100 coverage.