Found while writing content/docs/automation/email-templates.mdx (#10211), by checking the example corpus for a real authored template to model the guide's samples on.
The defect
examples/app-showcase/src/system/emails/index.ts declares:
exportconstTaskDoneEmail={name: 'showcase_task_done_email',// …locale: 'en',// …};sendTemplate's locale ladder is exact match, then en-US, then (no-locale calls only) the bundle's lowest tag — deliberately with no language-prefix matching, so en does not satisfy en-US and vice versa. The ladder is documented on SendTemplateInput.locale in packages/spec/src/contracts/email-service.ts and pinned by packages/plugins/plugin-email/src/template-locale-resolution.test.ts.
Consequences for this row:
| call | outcome |
|---|
sendTemplate({ template: 'showcase_task_done_email' }) (no locale) | resolves — rung 3, because the bundle has no en-US row |
sendTemplate({ template: 'showcase_task_done_email', locale: 'en-US' }) | TEMPLATE_NOT_FOUND |
notify-node delivery for a recipient whose locale is en-US | TEMPLATE_NOT_FOUND |
The third one is the one that matters: the notify node's template path resolves (name, recipient locale)per recipient at delivery time, so this template is unreachable for exactly the recipients the showcase is most likely to have. It is latent today only because nothing in the showcase currently references the template by name — the file's own comment calls it "Email template fired by the Task Completed flow", but grep finds no consumer, so the example is both mis-localed and unwired.
The second half — why the schema never caught it
The literal is not wrapped in defineEmailTemplateDefinition(...), so EmailTemplateDefinitionSchema.parse() never runs at authoring time. The schema would not have rejected locale: 'en' either (it is z.string(), not a BCP-47 validator) — but the factory is what applies the en-US default, and an author who omitted locale entirely would have landed on the resolvable value for free.
Worth noting the example corpus is what customers and AI authors copy, and the docs page added in #10211 explicitly warns that zh does not resolve zh-CN — the showcase currently demonstrates the anti-pattern the guide warns about.
Suggested fix
Set locale: 'en-US' (or drop the key and take the default), and wrap the literal in defineEmailTemplateDefinition(...) the way examples/app-showcase/src/automation/jobs/index.ts already wraps its job in defineJob(...). Whether the template should also be wired to the Task Completed flow's notify node is a separate call for whoever owns the showcase.
No label applied — leaving severity to triage. Filed unassigned from the #10211 docs card, whose surface is content/docs/automation/** only.
Found while writing
content/docs/automation/email-templates.mdx(#10211), by checking the example corpus for a real authored template to model the guide's samples on.The defect
examples/app-showcase/src/system/emails/index.tsdeclares:sendTemplate's locale ladder is exact match, thenen-US, then (no-locale calls only) the bundle's lowest tag — deliberately with no language-prefix matching, soendoes not satisfyen-USand vice versa. The ladder is documented onSendTemplateInput.localeinpackages/spec/src/contracts/email-service.tsand pinned bypackages/plugins/plugin-email/src/template-locale-resolution.test.ts.Consequences for this row:
sendTemplate({ template: 'showcase_task_done_email' })(no locale)en-USrowsendTemplate({ template: 'showcase_task_done_email', locale: 'en-US' })TEMPLATE_NOT_FOUNDen-USTEMPLATE_NOT_FOUNDThe third one is the one that matters: the notify node's template path resolves
(name, recipient locale)per recipient at delivery time, so this template is unreachable for exactly the recipients the showcase is most likely to have. It is latent today only because nothing in the showcase currently references the template by name — the file's own comment calls it "Email template fired by the Task Completed flow", but grep finds no consumer, so the example is both mis-localed and unwired.The second half — why the schema never caught it
The literal is not wrapped in
defineEmailTemplateDefinition(...), soEmailTemplateDefinitionSchema.parse()never runs at authoring time. The schema would not have rejectedlocale: 'en'either (it isz.string(), not a BCP-47 validator) — but the factory is what applies theen-USdefault, and an author who omittedlocaleentirely would have landed on the resolvable value for free.Worth noting the example corpus is what customers and AI authors copy, and the docs page added in #10211 explicitly warns that
zhdoes not resolvezh-CN— the showcase currently demonstrates the anti-pattern the guide warns about.Suggested fix
Set
locale: 'en-US'(or drop the key and take the default), and wrap the literal indefineEmailTemplateDefinition(...)the wayexamples/app-showcase/src/automation/jobs/index.tsalready wraps its job indefineJob(...). Whether the template should also be wired to the Task Completed flow's notify node is a separate call for whoever owns the showcase.No label applied — leaving severity to triage. Filed unassigned from the #10211 docs card, whose surface is
content/docs/automation/**only.