Commit 78020b8
committed
feat(email): materialize declared email_template metadata into sys_email_template (#4509)
Authoring an `email_template` was a silent no-op. `sendTemplate` resolves
`(name, locale)` against sys_email_template ROWS, whose only writers were the
built-in auth templates plus a code-constructed `EmailServicePluginOptions.
templates` that no bootstrapper passed — while every door an author can use
(stack `emailTemplates:`, `*.email-template.ts`, Studio, PUT /meta) parked items
in a metadata store nothing read back. An admin could "fix" the password-reset
mail in Studio and users kept receiving the built-in copy: ADR-0078 false
compliance on AUTH mail. Webhook #3461's shape, closed the same way (ADR-0049
enforce-or-remove, route: enforce).
`bootstrapDeclaredEmailTemplates` materializes declared items at boot, validating
each through EmailTemplateDefinitionSchema.parse() and projecting with
`mapTemplateToRow` — the SAME mapping the built-in seeder uses, extracted and
shared so the two doors cannot drift. Because email_template is
allowRuntimeCreate:true (unlike webhook), the plugin also subscribes to metadata
changes and re-materializes the changed item, so a Studio save takes effect
without a restart; a withdrawn template deactivates its rows rather than losing
them.
Three breaks sat on this path, and closing any two would still have shipped a
template that never sent:
- objectql never registered a manifest's `emailTemplates:` into the registry at
all — the key was missing from the generic ingestion list, so the bridge's own
source was empty.
- Built-in seeds left `managed_by` at the column's 'admin' default, masquerading
as admin-authored; since the bridge refuses to overwrite admin rows, a built-in
would permanently outrank the template an app declared. Built-ins now stamp
'platform'.
- Nothing materialized declared metadata into rows.
Seed-not-clobber mirrors sys_webhook (#3489): sys_email_template gains
managed_by/customized, declared rows re-seed as 'package', and an admin-created
or admin-edited row is never overwritten. Separate axis from `is_system`, which
keeps its meaning for built-ins.
Ledger email_template flips 13 dead → fully live, with an ADR-0054 proof bound on
`subject`: it boots a real stack, authors a template overriding a built-in auth
template, and asserts the AUTHORED wording reaches the transport.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5CYr5SDwe85gH2Jr5KSgu1 parent 48fbacb commit 78020b8
17 files changed
Lines changed: 1203 additions & 68 deletions
File tree
- .changeset
- packages
- cli/src/utils
- objectql/src
- platform-objects/src/audit
- plugins/plugin-email/src
- qa/dogfood
- test
- fixtures
- spec
- liveness
- scripts/liveness
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
284 | 284 | | |
285 | 285 | | |
286 | 286 | | |
287 | | - | |
288 | | - | |
289 | | - | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
290 | 295 | | |
291 | 296 | | |
292 | 297 | | |
| |||
295 | 300 | | |
296 | 301 | | |
297 | 302 | | |
298 | | - | |
299 | | - | |
300 | | - | |
| 303 | + | |
301 | 304 | | |
302 | 305 | | |
303 | 306 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1632 | 1632 | | |
1633 | 1633 | | |
1634 | 1634 | | |
| 1635 | + | |
| 1636 | + | |
| 1637 | + | |
| 1638 | + | |
| 1639 | + | |
| 1640 | + | |
1635 | 1641 | | |
1636 | 1642 | | |
1637 | 1643 | | |
| |||
Lines changed: 34 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
145 | 145 | | |
146 | 146 | | |
147 | 147 | | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
148 | 182 | | |
149 | 183 | | |
150 | 184 | | |
| |||
0 commit comments