Skip to content

docs(email-templates): copy the folded locale ladder of record into three stale texts - #29498

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-e5rt73
Nov 2, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-e5rt73

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Warren · project thread
Fixes #29032

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:邮件模板 locale 解析的三处旧文字(文档页、metadata-core 文档注释、平台清单条目)改为照抄 spec 的 SendTemplateInput.locale 阶梯;无代码改动。
  • 为什么改:Email template resolver ignores i18n.fallbackLocale and does no language-subtag folding: en-GB / zh-TW recipients never reach en / zh-CN rows #28880 起内置加载器会折叠语言子标签(zh → zh-CN)并先试声明的回退语言,三处仍写「精确匹配、不折叠」,QA 跑 main 会判错。
  • 风险与回滚:仅文字与清单条目;回滚即 revert 本 PR。metadata-core 的注释随 dist/index.d.ts 发布,故带 patch changeset。
  • 先看:docs/qa/platform-checklist/areas/integration-system.json(acceptance[3] 条款与 verify 行)、content/docs/automation/email-templates.mdx(Locale resolution 节)。
  • 两分钟验证:pnpm check:platform-checklist,预期 exit 0。
  • 需要你做的:无,席位落地。

What changes

Three texts outside packages/spec described the exact (name, locale) ladder that #28880 retired. Each now follows SendTemplateInput.locale in packages/spec/src/contracts/email-service.ts (landed with PR #29014), and keeps en and en-US as two rows of one bundle.

  • content/docs/automation/email-templates.mdx, Locale resolution: the ladder is copied from the spec docblock: the named-locale ladder over the built-in loader (fold by resolveBundleLocale, then the declared fallback locale folded the same way, then en-US, then TEMPLATE_NOT_FOUND), the replacement-loader exact ladder, and the no-locale ladder. The page names the spec docblock as the record.
  • packages/metadata-core/src/item-key-discriminators.ts: the docblock no longer says the resolution is exact with no folding. It keeps its own rule of citing rather than quoting: it cites SendTemplateInput.template and SendTemplateInput.locale and keeps the one point the table rests on, that every rung loads one row at one tag exactly, so folding picks a row and never merges two.
  • docs/qa/platform-checklist/areas/integration-system.json, item integration-system.email-template-render: acceptance[3]'s clause copies the ladder; its verify line now scores a zh recipient getting the zh-CN row as the pass (it scored en-US before) and a ja-JP recipient getting en-US through the showcase's declared fallback en; steps[4] and variants[0] add those recipients; automated.ref no longer calls the uncovered half "the no-folding case" and names plugin-email's fold pins below the oracle's layer. Revision 12 → 13, one history row (ref Email template locale ladder: three texts outside packages/spec still say the resolver matches exactly with no language-subtag folding (stale since #28880) #29032).
  • .changeset/29032-email-template-locale-ladder-docblock.md: @objectstack/metadata-core patch. The docblock ships: after pnpm --filter '@objectstack/metadata-core...' build, its text is in dist/index.d.ts and dist/index.d.cts (grep -rl 'That resolution is exact' packages/metadata-core/dist matched both on the base tree; positive control ITEM_KEY_DISCRIMINATORS matched index.js, index.cjs and both .d.ts files).

Why

A QA run against main reads the checklist clause and scores the folding behaviour as a FAIL, and authors reading the docs page are told zh never reaches zh-CN. The producer is EmailService.resolveAndRenderTemplate in packages/plugins/plugin-email/src/email-service.ts, which matches the spec docblock (read at base 8ca9dd7b).

Tests

No behaviour changed, so there is no new test and no ablation. The ladder the texts now describe is pinned by packages/plugins/plugin-email/src/template-locale-fold.test.ts and template-locale-fold.integration.test.ts, untouched here.

Acceptance notes

  • The TemplateLoader interface docblock in packages/plugins/plugin-email/src/email-service.ts still says the loader resolves "under the exact rule below — there is no best match and no language-subtag folding". That stays true of load() itself (each call loads one tag exactly; the fold lives in the service), so it is not one of the stale texts and is left alone.
  • The docs page and checklist clause cite the issue-free spec docblock as the record, so the next rewording on the spec side is one place to re-copy.
  • pnpm check:dual-build-cjs-loads was NOT MEASURED locally (exit 3, PREREQUISITE NOT MET: it loads every package's built output and this worktree built only the closures below). It reads no file this diff changes beyond a comment in metadata-core; CI's Lint & Repo Gates runs it.

Verification

All readings at head 4a0e6715, worktree off base 8ca9dd7b.

dispatch-gates: 82 derived, 81 exit 0, 1 NOT MEASURED
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 82 commands. Each was run with its exit code captured before any pipe.
  • 78 exited 0 on the first pass. Three exited 3 (prerequisite) and exited 0 on a rerun: check-plugin-teardown-shape.mjs --self-test (after fetching its pinned fixture commit), spec check:skill-examples (after building @objectstack/client and client-react) and check:lean-entry-closure (after building @objectstack/objectql).
  • pnpm check:dual-build-cjs-loads: exit 3, NOT MEASURED (see Acceptance notes).
  • dispatch-gates --ran: "82 derived famil(ies) accounted for — 81 run, 1 NOT-MEASURED", exit 0.
  • pnpm check:platform-checklist: exit 0 (in the 81).
docs build and the published docblock
  • Docs build, the production command from apps/docs/vercel.json under TURBO_FORCE=true: Tasks: 2 successful, 2 total, Cached: 0 cached, and apps/docs/.next/BUILD_ID present.
  • pnpm --filter @objectstack/metadata-core build && pnpm --filter @objectstack/metadata-core typecheck: VERDICT command-exit 0. After the rebuild, dist/index.d.ts and dist/index.d.cts carry the new docblock and no longer contain "That resolution is exact".

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind. (no names added)
  • 3. No formatter ran; pnpm lint passes. (no lint-covered source changed; CI runs it)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: n/a.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the 维护者速读 block is the os-dev contract's Chinese summary).

Generated by Claude Code

…hree stale texts

The email-templates docs page, the metadata-core ITEM_KEY_DISCRIMINATORS
docblock and the integration-system.email-template-render checklist clause
still described the exact (name, locale) ladder with no language-subtag
folding. They now copy (or, for the docblock, cite) SendTemplateInput.locale
in packages/spec/src/contracts/email-service.ts, keeping en and en-US as two
rows of one bundle. The checklist item moves to revision 13 with one history
row.

Claude-Session: https://claude.ai/code/session_01JQwxbWaao8NE8qbxe4dXUL
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/metadata-core/src/item-key-discriminators.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/metadata-core/src/item-key-discriminators.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 5 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 94d3e2543488fceda39fbfe88036bb6887037390 → packageMentionDocs.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:devx#1 · os-warren · session_01JQwxbaBjisxkQo9hht9bFB

维护者速读

  • 改了什么:邮件模板 locale 解析的三处旧文字(文档页 Locale resolution 节、metadata-core 的 ITEM_KEY_DISCRIMINATORS 文档注释、平台清单 integration-system.email-template-render)改为照抄 spec SendTemplateInput.locale 的阶梯;无代码改动。
  • 为什么改:Email template resolver ignores i18n.fallbackLocale and does no language-subtag folding: en-GB / zh-TW recipients never reach en / zh-CN rows #28880 起内置加载器会折叠语言子标签(zh → zh-CN)并先试声明的回退语言,三处仍写「精确匹配、不折叠」,QA 跑 main 会把正确行为判 FAIL。
  • 风险与回滚:只有文字与一个清单条目;注释随 dist/index.d.ts 发布,故 @objectstack/metadata-core 带 patch changeset。回滚即 revert。
  • 先看:清单条目 acceptance[3] 的 clause 与 verify 行,其次文档页 Locale resolution 节。
  • 两分钟验证:pnpm check:platform-checklist,预期 exit 0。
  • 需要你做的:无,CI 全绿后席位落地。
  • 席位意见:文档页与清单条款逐句对过 packages/spec/src/contracts/email-service.ts 的 SendTemplateInput.template / .locale,是照抄不是改写;verify 行把「zh 拿到 en-US」改判为 FAIL,与折叠规则一致。

席位核对

ACCEPT · domain:devx#1 · head 4a0e6715b05e29539b5716ad6080297af9238eb1 · 2026-11-02T16:34Z

  1. Shape: four files (+65 / -30): content/docs/automation/email-templates.mdx (the Locale resolution section), the docblock in packages/metadata-core/src/item-key-discriminators.ts (comment only), the item integration-system.email-template-render in docs/qa/platform-checklist/areas/integration-system.json (variants[0], steps[4], acceptance[3] clause and verify, automated.ref, revision 12 → 13, one history row ref Email template locale ladder: three texts outside packages/spec still say the resolver matches exactly with no language-subtag folding (stale since #28880) #29032), and a new .changeset/29032-email-template-locale-ladder-docblock.md (patch). Inside the claimed surface (6581833799); the changeset is the claim's "only if the docblock ships" branch, measured by the dev in dist/index.d.ts and index.d.cts.
  2. Premise, measured: on origin/main the seat read SendTemplateInput in packages/spec/src/contracts/email-service.ts: the name matched exactly with one row per tag (template), the four named-locale rungs (fold by resolveBundleLocale, declared fallback folded the same way, en-US exactly, TEMPLATE_NOT_FOUND), the replacement-loader exact ladder and the no-locale ladder (locale). The page and the clause copy those sentences; the docblock cites them. examples/app-showcase/objectstack.config.ts:193 declares fallbackLocale: 'en', which the new verify line relies on; plugin-email/src/template-locale-fold.test.ts and .integration.test.ts, named in automated.ref, exist on main.
  3. Gates: local 82 derived, 81 exit 0, 1 NOT MEASURED (check:dual-build-cjs-loads, exit 3 on unbuilt packages, declared in the PR body and run by CI); check:platform-checklist exit 0; docs build green (report 6582500135). The seat arms only when every check-run on this head is green or an expected skip.
  4. Accepted deviations: Fixes / Clause-② / Metadata-protocol sit above ## 维护者速读 (os-dev order). The docblock keeps its own cite-not-quote rule instead of copying the ladder, which is what the card's "copy that text" means for a file whose rule is to cite.
  5. Out of scope: none found by the dev or the seat. Skills-docs: n/a (Metadata-protocol: none). Prose faces checked at this tier: the page and the clause, against the spec sentences above.
  6. Landing: not governed (content/docs/**, docs/qa/**, a comment in packages/metadata-core), under 3000 changed lines, Clause-②: no, no contract review owed (no schema, no governed path, no narrowing). Seat lands it: pr_ready + automerge_enable once every check on this head is green or an expected skip.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 2, 2026 16:59
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 2, 2026 16:59
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit 6a940d1 Nov 2, 2026
51 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-e5rt73 branch November 2, 2026 17:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

1 participant