swap-notification-templates
Load notification templates from a database or CMS instead of code (editable emails and push copy, per-tenant branding). Use when the user wants non-developers to edit notification content or brand it
SKILL.md.claude/skills/swap-notification-templates/SKILL.md
GitHubSwap the notification template source
- Interface:
TemplateSource(abstract class,bind(payload): Promise<BoundTemplate>) inapps/notifications/src/dispatch/templates.ts. - Today:
CodeTemplateSource: thetemplatesregistry in the same file, rendering React Email components frompackages/emailwith copy frompackages/i18n. - Bound in:
apps/notifications/src/dispatch/notifications.module.ts({ provide: TemplateSource, useClass: CodeTemplateSource }). - Env: none today.
Swap
- Store templates (db-change skill; the
notificationsschema is the service's own). - Implement
TemplateSource: look up the stored template forpayload.template(and the organization, for branding), cache it in Redis, and return aBoundTemplatewith the samecategoryand only the channels it renders. Fall back toCodeTemplateSourcewhen no stored version exists, so every template keeps working. - Bind the new class in
notifications.module.ts. The dispatcher, channels and preferences don't change. - Validate stored content before rendering: the fields it may use are the template's
payload schema in
packages/jobs/src/queues.ts. Keep usingcontext.unsubscribeUrlin emails the recipient can opt out of (the dispatcher supplies it).
Tests
apps/notifications/test/notifications.integration.test.ts sends every channel through
Mailpit and the fake push and SMS servers; add cases for a stored template and for the
fallback. packages/email/src/render.test.tsx covers the code templates.
Finish
- The verify skill.
- Ask the
revieweragent to review the change, and thesecurity-revieweragent: a new implementation brings its own credentials and sends data somewhere new. If you wrote a migration, themigration-revieweragent too. - Update the seam's row in the README's "Scaling path" table if what's "Now" changed.