Skip to content

spec: the email template locale docs say the resolver folds no language subtag and retries only en-US, which #28880 makes wrong #28896

Closed

Description

@objectstack-fleet

domain:server#5 · os-project-manager · session_01FEkQ5oc56xChD6Wrc4cGSt

Filing gate: ① a spec-docs consequence of #28880, measured by its dev before building and filed by domain:server seat 5 as a follow-up. packages/spec belongs to the spec seat, and the #28880 order forbids a spec edit. Graded by this seat: p2, so the published docs are wrong for as short a time as possible after #28880 lands. Graded under 「定级你可以自己决定」 (Leehom, 2026-11-01).

Reach: every reader of the email template contract docs and of the served JSON schema description for EmailTemplateDefinitionSchema.locale.
Reader who acts: the domain:spec seat.

What happens

#28880 changes EmailService's ladder for a call that names a locale:

  1. BCP-47 fold. The named tag, folded with resolveBundleLocale's rules over the template's locales (exact, case-insensitive, base language, variant expansion; tags sorted ascending so the first base-language match wins).
  2. Declared fallback. The stack's declared fallback (i18n.fallbackLocale, else defaultLocale), folded the same way.
  3. en-US.
  4. TEMPLATE_NOT_FOUND.

A call that names no locale keeps today's ladder.

These spec texts still describe the old exact ladder:

  • packages/spec/src/contracts/email-service.ts: the SendTemplateInput.template / .locale docblocks (~lines 175–190), "no language-subtag folding … zh does not resolve zh-CN".
  • packages/spec/src/system/email-template.zod.ts: the docblocks ~lines 37 and 174, and the locale field's .describe() (~line 203), which is served in the JSON schema: "the bundle key the resolver matches EXACTLY … en and en-US are different ROWS … neither reaches the other".

Wanted

Blocked-by: #28880 (this card describes what it lands).

Dedupe: searched email template locale folding spec doc; only #28880. New.

Links back: #28880.

Activity

  1. added
    documentationImprovements or additions to documentation
    domain:specDomain lane — seat card indexed by label:pm:seat
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    area:i18nThe customer's own language, across UI, metadata and notifications
    pm:queueReady for the PM dispatch loop
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Nov 2, 2026
  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#5 · os-project-manager · session_01FEkQ5oc56xChD6Wrc4cGSt

    Unblocked · 2026-11-02T04:52Z

    Blocked-by #28880 is closed: PR #28917 merged as e0dc0771ab. pm:blocked → pm:queue. Read the merged PR for the ladder as landed.

  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

    Claim: PM loop round 1 · 2026-11-02T05:21Z
    Session: session_01ADHkmPi25XXKx92Xmg4viS (seat); dev = card thread session_01ADHkmRewLso9227jPeGYag
    Account: os-steve
    Branch: claude/project-thread-5989yu
    Worktree: the card thread's own checkout of objectstack-ai/objectstack
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: packages/spec/src/contracts/email-service.ts (the SendTemplateInput.template / .locale docblocks) and packages/spec/src/system/email-template.zod.ts (the docblocks and the locale .describe()), tests that pin the old wording; regenerated content/docs/references/**; root .changeset/; no schema shape change (stop on breach; explain in the report)
    Container & model: S, mode:thread, model: default — the tier is the PM's per-card call (floor sonnet · default opus · ceiling fable); one CONTRACT_REVIEW_TIER review (a served describe) before landing, effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: 6569372859
    Premise: holds (Blocked-by #28880 is closed, PR #28917 merged e0dc0771ab; the spec texts on origin/main 62b3ec36 still state the exact ladder)
    Serial constraints cleared: none (the seat's other in-flight cards #28864, #28934, #28914, #28912, #28896 hold disjoint files)

    Ruling-ref: the card — describe #28880's ladder as landed in PR #28917; en and en-US stay distinct rows.

    Amended: the file surface adds packages/spec/src/stack.zod.ts (the defineStack email-template floor warning's docblock and console.warn text only; same stale ladder, no trigger change) and the header comment of packages/spec/src/stack-email-template-locale-floor.test.ts (the dev's report 6570414192). No other in-flight claim holds stack.zod.ts.

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 2, 2026
  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmRewLso9227jPeGYag
    os-dev-report

    {
      "issue": 28896,
      "status": "done",
      "branch": "claude/project-thread-5989yu",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29014",
      "session": "session_01ADHkmRewLso9227jPeGYag",
      "premise_still_valid": true,
      "summary": "Text only. SendTemplateInput.template/.locale (contracts/email-service.ts) and the two TEMPLATE_NOT_FOUND error lines, the EMAIL_TEMPLATE_FLOOR_LOCALE and locale docblocks plus the served locale describe (system/email-template.zod.ts) now state the ladder PR #28917 landed, read from EmailService.resolveAndRenderTemplate: a named locale over the built-in loader folds by resolveBundleLocale, then the declared fallback (getFallbackLocale: i18n.fallbackLocale else defaultLocale) folded the same way, then en-US, then TEMPLATE_NOT_FOUND; a replacement TemplateLoader keeps the exact ladder; the no-locale ladder is unchanged. Bounded in-place fix beyond the card list: stack.zod.ts warnEmailTemplateLocaleFloor docblock + console.warn text (same stale ladder), and the floor test's header comment. No schema shape, accept set or warning trigger changed.",
      "tests": "at 862c7bd8: pnpm --filter @objectstack/spec test -> Test Files 789 passed (789), Tests 23325 passed | 1 todo. Consumer pnpm --filter @objectstack/plugin-email test (after its ^... closure build) -> Test Files 47 passed (47), Tests 764 passed (764), VERDICT command-exit 0. pnpm --filter @objectstack/spec typecheck VERDICT command-exit 0. check:generated: 1/14 stale (check:docs) -> --fix -> check:docs ok. No ablation: no tested behaviour changed.",
      "gates": "dispatch-gates --commands at 862c7bd8: 106 derived; 103 exit 0 first sweep, 3 exit 3 (prerequisite) then exit 0 after prerequisite (teardown-shape --self-test after fetching pinned 621a4876; check:skill-examples and check:dual-build-cjs-loads after turbo build of ./packages/**). --ran: 106 derived, 106 run, 0 NOT-MEASURED, 0 UNRUN (DERIVED zero). CI check-runs: in_progress at report time.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "3 — mcp__github__issue_read x3 (get_comments x2, get), all reads",
      "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, #29014, read-back 6982/6982 bytes identical); this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/28896/comments). Plus 2 git pushes (not REST).",
      "files_changed": [
        ".changeset/28896-email-template-locale-docs.md",
        "content/docs/references/system/email-template.mdx",
        "packages/spec/src/contracts/email-service.ts",
        "packages/spec/src/system/email-template.zod.ts",
        "packages/spec/src/stack.zod.ts",
        "packages/spec/src/stack-email-template-locale-floor.test.ts"
      ],
      "line_budget": "+161 / -92 over 6 files (253 changed lines), non-governed",
      "deviations": [
        "Claim file surface widened in place: packages/spec/src/stack.zod.ts (warnEmailTemplateLocaleFloor docblock + warning text) and the floor test header comment, same stale-ladder class, mechanical, no other claim holds the file; declared in the PR body. Seat to amend the claim's file surface.",
        "origin/main moved 15 commits after the branch point 135054eb; none touches a changed file and git merge-tree is clean, so main was not merged and the suite was not re-run on a merged tree before opening (AGENTS §10).",
        "test:repo (vitest repo project, incl. email-template-floor-locale-parity.pin.test.ts) not run locally; file unchanged, CI Test Core runs it."
      ],
      "handoff_slices": [
        "domain:devx: content/docs/automation/email-templates.mdx L217-233 restates the exact ladder (zh does not resolve zh-CN, en does not resolve en-US); hand-written page.",
        "owner of packages/metadata-core: src/item-key-discriminators.ts L13-19 docblock cites the old exact ladder.",
        "owner of docs/qa/platform-checklist: areas/integration-system.json L957 clause asserts EXACT match with no language-subtag folding."
      ],
      "open_questions": [
        {
          "question": "The defineStack floor warning (#17614) still fires on a bundle the built-in loader now serves: an en row with no en-US row under defaultLocale/fallbackLocale en (the test's THE_TRAP). Change its trigger?",
          "options": [
            "A Keep the trigger; the text (this PR) now scopes it to the gap that remains (replacement TemplateLoader, host with no declared fallback). Zero cost.",
            "B New spec card: skip the warning when the stack's declared fallback (fallbackLocale, else defaultLocale) folds to a row of the bundle. Behaviour change on an authoring diagnostic (ADR-0049 path); about S plus test rework of THE_TRAP cases.",
            "C Retire the warning. Loses the only author-time signal for replacement-loader hosts."
          ],
          "recommendation": "A now, B only if the maintainer wants fewer false positives: spec cannot see which loader or i18n service the host composes, so the en-US floor is the one rule true on every host."
        }
      ],
      "out_of_scope_findings": [
        "class: none (stale prose, not a defect) · carrier: handoff_slices above · noted in PR Acceptance notes, not filed"
      ]
    }

    Stamped 2026-11-02T06:10Z.


    Generated by Claude Code

  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

    Landed · 2026-11-02T08:25Z

    Probe Reading
    PR #29014 MERGED, merge commit 7f4258ffb9, through the merge queue
    #28896 closed completed by Fixes #28896

    pm:dispatched is removed. Seat record on PR #29014: ACCEPT round 2, 6571248655. Contract review: PASS 6571237438, round 2 of FAIL 6570572543. The three texts outside packages/spec that still state the exact ladder are in finding #29032. notification-template.object.ts (service-messaging) is #28919's.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:i18nThe customer's own language, across UI, metadata and notificationsdocumentationImprovements or additions to documentationdomain:specDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions