Skip to content

Commit 354dfcd

Browse files
docs(spec): IEmailService Errors lists name TEMPLATE_INVALID (#26911)
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} --> _Requested by **Yibo Wang** · [project thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBDcxHMZCUNuUDSn6AKJTHtZ)_ Fixes #26803 ## 维护者速读 - **改了什么**:`IEmailService` 契约里 `sendTemplate` 和 `renderTemplate` 的错误清单补上 `TEMPLATE_INVALID`,只改注释,不改类型或 schema。 - **为什么改**:插件已经会对含非法占位符的已存模板抛 `TEMPLATE_INVALID`,但契约没写,渠道或应用作者读契约时不知道这是一个永久失败。 - **风险与回滚**:纯文档改动,无运行时影响;回滚即还原这一个提交。 - **先看**:`packages/spec/src/contracts/email-service.ts`(两段新增的 Errors 条目)。 - **两分钟验证**:`git grep -c TEMPLATE_INVALID -- packages/spec/src/contracts/email-service.ts`,预期 4(每个清单两处)。 - **需要你做的**:无,席位落地。 Clause-②: no Metadata-protocol: none Skills-docs: no skill lists these codes; `content/docs/kernel/runtime-services/email-service.mdx` (Typed Error Codes) updated in this PR; `content/docs/automation/email-templates.mdx` already names it. ## What changes - `packages/spec/src/contracts/email-service.ts`: the `sendTemplate` and `renderTemplate` Errors lists name `TEMPLATE_INVALID`, worded from what `@objectstack/plugin-email` does: a stored template with a hole the authoring rules refuse (rule `email-template-hole-grammar`: a second formatter, or a `currency` argument that is not a literal ISO 4217 code) is refused at render with `TEMPLATE_INVALID: email template NAME (locale=LOCALE): HOLE_AND_RULE`, and is permanent until the template is re-published. - `content/docs/kernel/runtime-services/email-service.mdx`: Typed Error Codes lists `TEMPLATE_INVALID`. - `.changeset/26803-email-service-template-invalid.md`: patch on `@objectstack/spec`. ## Why The runtime throws this code from both methods since #26799, and `service-messaging` grades it permanent, but the contract a channel or app author reads did not list it. ## Measured - `TEMPLATE_INVALID` on `origin/main` 6090bb7: thrown by `template-engine.ts` render (`TEMPLATE_INVALID: ` + the grammar's refusal) and re-thrown by `EmailService.resolveAndRenderTemplate` with the template name and locale in front; both `sendTemplate` and `renderTemplate` go through that resolver. - `TEMPLATE_UNSUPPORTED`: `EmailService` never throws it. Only the `service-messaging` email and inbox channels raise it, when the registered email service lacks `sendTemplate` / `renderTemplate`. So it is not added to `IEmailService`'s lists. - Error-code ledger: does not apply. `error-code-ledger.zod.ts` registers route `error.code` values; `TEMPLATE_NOT_FOUND`, `TEMPLATE_INACTIVE` and `MISSING_VARIABLES` are absent from it (0 hits) because they are `Error` message prefixes, not envelope codes. Mirroring that, `TEMPLATE_INVALID` is not added, and the ledger and its test are untouched. ## Acceptance notes - `content/docs/kernel/runtime-services/email-service.mdx` Typed Error Codes now lists `TEMPLATE_INVALID` beside the three older codes (claim surface amended by the seat, 2026-10-27T20:51Z). ## Verification All on head 6978e58 (spec tests on 0ca6a37; the later commit is .mdx only). <details><summary>Spec tests and typecheck (under os-verify-lock)</summary> `pnpm --filter @objectstack/spec test && pnpm --filter @objectstack/spec typecheck`: VERDICT command-exit 0. Test Files 736 passed (736); Tests 22050 passed, 1 todo. </details> <details><summary>Derived gates</summary> `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`: On 6978e58: 97 derived, 94 exit 0, 3 NOT MEASURED with exit 3 (full-build prerequisite: spec `check:skill-examples`, `check:dual-build-cjs-loads`, `check:lean-entry-closure`); `--ran`: 94 run, 3 NOT-MEASURED, 0 UNRUN. Earlier, on 0ca6a37: 73 commands. 71 exit 0. `check:doc-formula-expressions` first exited 3 (formula not built), then exit 0 after building formula and lint. `check:dual-build-cjs-loads` and `check:lean-entry-closure` exit 3 (PREREQUISITE NOT MET, need a full build): NOT MEASURED locally, CI runs them. `--ran` reconciliation: 73 derived, 71 run, 2 NOT-MEASURED, 0 UNRUN. </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01WGufziYqBAqnKprHc4h25c --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4049efa commit 354dfcd

3 files changed

Lines changed: 23 additions & 0 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
docs(spec): `IEmailService.sendTemplate` and `renderTemplate` list `TEMPLATE_INVALID` in their Errors
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
`@objectstack/plugin-email` already refuses a stored template whose hole the authoring rules refuse (rule `email-template-hole-grammar`), throwing `TEMPLATE_INVALID: email template NAME (locale=LOCALE): HOLE_AND_RULE` from both methods. The contract's Errors lists now name that code beside `TEMPLATE_NOT_FOUND`, `TEMPLATE_INACTIVE` and `MISSING_VARIABLES`, so a channel or app author reading `IEmailService` knows the failure is permanent until the template is re-published. Documentation only: no type or schema changes.

‎content/docs/kernel/runtime-services/email-service.mdx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ services.email.sendTemplate(input: SendTemplateInput): Promise<SendEmailResult>
3131
- `TEMPLATE_NOT_FOUND`
3232
- `TEMPLATE_INACTIVE`
3333
- `MISSING_VARIABLES`
34+
- `TEMPLATE_INVALID`: the stored template has a hole the authoring rules refuse (a second formatter, or a `currency` argument that is not a literal ISO 4217 code). It is permanent: retrying fails until the template is re-published without that hole. `renderTemplate` raises it too.

‎packages/spec/src/contracts/email-service.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,12 @@ export interface IEmailService {
318318
* (for a call that named no locale) the name carries no members at all.
319319
* - `TEMPLATE_INACTIVE` — the template exists but `active=false`.
320320
* - `MISSING_VARIABLES` — declared `required` variables absent from `data`.
321+
* - `TEMPLATE_INVALID` — the stored template has a hole the authoring rules
322+
* refuse (rule `email-template-hole-grammar`: a second formatter, or a
323+
* `currency` argument that is not a literal ISO 4217 code). It is refused
324+
* at render, as `TEMPLATE_INVALID: email template <name> (locale=<l>):
325+
* <hole and rule>`, so nothing is delivered. Permanent: retrying cannot
326+
* succeed until the template is re-published without that hole.
321327
*/
322328
sendTemplate(input: SendTemplateInput): Promise<SendEmailResult>;
323329

@@ -337,6 +343,12 @@ export interface IEmailService {
337343
* (for a call that named no locale) the name carries no members at all.
338344
* - `TEMPLATE_INACTIVE` — the template exists but `active=false`.
339345
* - `MISSING_VARIABLES` — declared `required` variables absent from `data`.
346+
* - `TEMPLATE_INVALID` — the stored template has a hole the authoring rules
347+
* refuse (rule `email-template-hole-grammar`: a second formatter, or a
348+
* `currency` argument that is not a literal ISO 4217 code). It is refused
349+
* at render, as `TEMPLATE_INVALID: email template <name> (locale=<l>):
350+
* <hole and rule>`, so nothing is rendered. Permanent: retrying cannot
351+
* succeed until the template is re-published without that hole.
340352
*/
341353
renderTemplate(input: RenderTemplateInput): Promise<RenderTemplateResult>;
342354

0 commit comments

Comments
 (0)