Files
suaveplan/openspec/changes/enterprise-server-sms/tasks.md
2026-05-27 23:29:53 +01:00

4.4 KiB

Tasks: enterprise-server-sms

Change ID: enterprise-server-sms Package: @suaveplan/sms Target path: server/services/sms Prerequisites on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/retry, @suaveplan/suppression, @suaveplan/idempotency, @suaveplan/templates, @suaveplan/logger


0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create directory tree per proposal.md.
  • package.json at 0.1.0, "sideEffects": false, exports "." + service + providers/in-memory.
  • Deps: all workspace + catalog deps as listed.
  • DevDeps: vitest catalog:testing, @vitest/coverage-istanbul catalog:testing, @suaveplan/testing workspace:^.
  • tsconfig.json extending library-node.json; vitest.config.ts with createTestConfig().
  • Scripts: lint, typecheck, test, test:coverage, build, docs:check.

2. Types and errors

  • Define all interfaces and Zod schemas (SmsMessageSchema, SmsServiceOptionsSchema) in src/types/sms.types.ts.
  • Write sms.types.md (≥200 words, both examples, API reference).
  • src/error-codes.ts: five codes; registerSmsErrorCodes(); eager call.

3. InMemory provider

  • Implement InMemoryProvider in src/providers/in-memory-provider/in-memory-provider.ts:
    • send(message): validate E.164 format; store in internal Map<messageId, SmsRecord>; return SmsSendResult with status: 'sent'.
    • getStatus(messageId): return from internal map or undefined.
    • setStatus(messageId, status): test helper to simulate delivery updates.
    • getSentMessages(): return all recorded messages (testing helper).
  • Write in-memory-provider.test.ts: send + getStatus round-trip; setStatus update; getSentMessages.
  • Write in-memory-provider.md (≥200 words).

4. SMS service

  • Implement SmsService in src/service/sms-service/sms-service.ts:
    • send(message):
      1. Validate message.to as E.164 (regex); throw SMS_INVALID_NUMBER if invalid.
      2. Check suppression via suppressionStore?.check(to); throw SMS_SUPPRESSED if suppressed.
      3. Idempotency: use message.idempotencyKey with @suaveplan/idempotency guard if key present.
      4. Call provider.send(message) with @suaveplan/retry; throw SMS_SEND_FAILED on exhaustion.
      5. Log result; OTel span sms.send; counter sms.sends_total with status.
    • sendBatch(messages): parallel sends with Promise.allSettled; collect results.
    • getStatus(messageId): delegate to provider.
    • handleInbound(payload): parse via InboundSmsSchema Zod schema; throw SMS_INBOUND_PARSE_FAILED on invalid.
    • handleStatusCallback(payload): parse via SmsDeliveryStatusSchema.
  • Write sms-service.types.ts.
  • Write sms-service.test.ts (≤500 lines): successful send; suppressed number skips; invalid number throws; retry on provider failure; batch; inbound parse; status callback parse; idempotency dedup.
  • Write sms-service.md (≥200 words, simple + advanced, API reference).

5. Integration tests

  • tests/integration.test.ts: SmsService + InMemoryProvider + real suppression store (from @suaveplan/suppression); send to suppressed number; send batch; verify delivery status updates; { timeout: 10000 }.

6. Docs + verify + publish

  • Root README.md: quick-start with InMemoryProvider for tests, provider implementation guide, API table.
  • bunx turbo lint --filter=@suaveplan/sms — zero errors.
  • bunx turbo typecheck --filter=@suaveplan/sms — zero errors.
  • bunx turbo test --filter=@suaveplan/sms — all pass.
  • bunx turbo test:coverage --filter=@suaveplan/sms — 100%.
  • bun scripts/publish.ts --package @suaveplan/sms.
  • Update CLAUDE.md; archive to openspec/archive/2026-04-24-enterprise-server-sms/.