3.3 KiB
3.3 KiB
Tasks: enterprise-api-plugin-notifications
Change ID: enterprise-api-plugin-notifications
Package: @suaveplan/api-plugin-notifications
Target path: api/comms/api-plugin-notifications
Prerequisite packages on Verdaccio: @suaveplan/api, @suaveplan/api-shared, @suaveplan/api-plugin-auth, @suaveplan/api-plugin-database, @suaveplan/templates, @suaveplan/suppression, @suaveplan/retry, @suaveplan/events, @suaveplan/rate-limiter, @suaveplan/error
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin 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.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
1. Scaffold + types + errors + events
- Create
api/comms/api-plugin-notifications/;package.jsonv0.0.0; standard tooling. - Types:
NotificationRecord,SendNotificationInput,NotificationQuery,NotificationPreferences,NotificationsPluginConfig(Zod). - Errors: 5-code union + metadata + registration. Test + doc.
- Events:
NotificationEventMap(notification:sent, notification:failed, notification:read).
2. SurrealQL
notification.surql,notification_preference.surql. tenantId + userId indexes.
3. Adapter ports
src/adapters/email.port.ts—EmailProviderinterface:send(to, subject, html, text).src/adapters/sms.port.ts—SmsProviderinterface:send(to, body).src/adapters/push.port.ts—PushProviderinterface:send(subscription, payload).- Each port:
.test.tsusing a real stub implementation (no mocks).
4. Services (each .test.ts + .md)
- NotificationService:
send— check preferences + suppression list; route to channel adapter(s); persistNotificationRecord; emit event. - DeliveryService: wraps each channel send with
@suaveplan/retry; on exhausted retries marks record asfailed; emitsnotification:failed. - PreferencesService:
getPreferences,updatePreferences.
5. Routes
GET /notifications—requireAuth; list + pagination.POST /notifications/:id/read—requireAuth.GET /notifications/unread-count—requireAuth.GET/PATCH /notifications/preferences—requireAuth.- Route
.test.ts+.md.
6. Plugin factory + integration tests + publish
createNotificationsPlugin:definePlugin,dependencies: ["database", "auth"],migrationsPath: "./surql".- Integration: send in-app notification → mark read → check unread count; suppressed email skipped (real SurrealDB).
bunx turbo lint typecheck test --filter=@suaveplan/api-plugin-notifications— green; 100% coverage.bun scripts/publish.ts --package @suaveplan/api-plugin-notifications.- Archive →
openspec/archive/2026-04-24-enterprise-api-plugin-notifications/.