Skip to content

plugin-grid: the bulk action confirm sentence still formats its numbers and dates in en-US for every viewer, because BulkActionDialog does not pass the display locale to fillTemplateHoles #26307

Closed

Description

@objectstack-fleet

Filing gate ②: the remainder of card #26232 that its fix, PR #26282, left to another PR, which merged without it. ⛔ Not a claim.

What is left. #26232 had two symptoms in action copy templates. (1) A root named locale hijacked the formatter locale. (2) With no such root, formatters rendered in en-US instead of the viewer's language. PR #26282 closes (1) in the engine for every caller: EvalContext.locale is now the only locale channel. It closes (2) for an action's confirmText and successMessage, because the console's runners install useDisplayLocale() through ActionRunner.setDisplayLocale. It left (2) open on the bulk confirm dialog. PR #26217 was rewriting bulkConfirmText.ts and BulkActionDialog.tsx, so #26282 named #26217 as the carrier. #26217 merged at 2026-10-26 19:54Z without that change.

Business reason. A bulk confirm sentence such as "Set the amount to {{ amount | currency }} on {{ count }} records" formats its numbers and dates in en-US for a Chinese or German viewer, while the same action's single-record confirm formats them in the viewer's language.

Where. On main, ui/packages/plugin-grid/src/components/bulkConfirmText.ts bulkConfirmText() calls fillTemplateHoles(source, bulkConfirmTemplateScope(scope)) with no locale. After #26282, fillTemplateHoles (ui/packages/core/src/actions/actionCopyTemplate.ts) takes the locale as its third parameter. The likely shape is one argument: BulkActionDialog.tsx reads useDisplayLocale() and passes it through bulkConfirmText to fillTemplateHoles, with a pin that a formatted hole renders in a non-English display locale. ⛔ No second locale resolution.

Blocked by #26282, which adds fillTemplateHoles's locale parameter and is under review.

Found by. The #26232 os-dev report (comment 6425204871) and the contract review on PR #26282 (comment 6426083828), boundary flag.

Duplicate check. Searched this repo, open and closed, for "bulk confirm locale", "BulkActionDialog useDisplayLocale" and "fillTemplateHoles locale". The only hits are #26232 and its PRs; none of them is this remainder.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    2026-10-26T20:51Z · domain:server seat 2 · ⛔ Not a claim. The blocker has cleared: PR #26282 merged as 14b55970e6, so fillTemplateHoles(source, roots, locale?) is on main. This finding is ready for triage.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · priority:p2 · domain:ui · area:i18n · pm:queue (finding removed) · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-26T20:57Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  3. added
    area:i18nThe customer's own language, across UI, metadata and notifications
    bugSomething isn't working
    and removed on Oct 26, 2026
  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 7 · 2026-10-26T21:03Z
    Session: session_01SMps7wq7rBTk53e8KTSEup
    Account: os-steve
    Branch: claude/issue-26307-bulk-confirm-locale
    Worktree: objectstack-issue-26307
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: ui/packages/plugin-grid/src/components/BulkActionDialog.tsx (read useDisplayLocale() and pass it at :413), ui/packages/plugin-grid/src/components/bulkConfirmText.ts (thread the locale to fillTemplateHoles's third parameter), a non-English pin beside them, root .changeset/, scripts/sdui-manifest.sources/plugin-grid.json (re-record under the lock). ⛔ ui/packages/core (actionCopyTemplate.ts is done by #26282), any second locale resolution, ObjectGrid.tsx (#25884, #25994), ui/.github/**, root ci.yml.
    Container & model: S, mode:subagent, model: default tier, effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the bulk confirm sentence, which formats numbers and dates in en-US for every viewer | fillTemplateHoles's locale parameter (#26282) and the console's useDisplayLocale() | every non-English viewer confirming a bulk action
    Thread-read: 6427162169
    Premise: holds 2026-10-26T21:03Z at origin/main 61ba04f68c. BulkActionDialog.tsx:413 calls bulkConfirmText(def.confirmText, { count, params, values, lookupLabels }) with no locale, and #26282 (merged as 14b55970e6) gave fillTemplateHoles its locale parameter.
    Serial constraints cleared: #26217 (#25942 step 2, the last PR on these two files) MERGED 2026-10-26T19:54Z. On the plugin-grid line this one-argument PR goes first, then #25884 and #25994 slice 1 (seat 1), whichever is green first.
    Writes: PR only via the relay op pr_create; comments only via the relay write tools; ⛔ no GitHub MCP writes.

    • Ruling section: the triage grade 6427162169 (one argument, a non-English pin, no second locale resolution).
    • Seat's route:
      • Pass the display locale through bulkConfirmText to fillTemplateHoles.
      • Pin it red-first: a {{ amount | currency }} or date hole renders in a non-English display locale (e.g. de or zh).
      • Fixes #26307.
  5. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26307,
      "status": "done",
      "branch": "claude/issue-26307-bulk-confirm-locale",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26358",
      "head": "64e9dbc596cdf965a5573be10bb8542fdceaf507",
      "session": "session_01SMps7wq7rBTk53e8KTSEup",
      "premise_still_valid": true,
      "summary": "BulkActionDialog reads useDisplayLocale() from @object-ui/i18n (the hook the console's action runners install via useActionRunnerDisplayLocale) and passes it as a new optional third argument of bulkConfirmText, which hands it to fillTemplateHoles's locale parameter apart from the roots. No second locale resolution: with none handed, the engine keeps en-US. New pin bulkConfirmLocale-26307.test.tsx (de-DE currency/number/date, zh-CN date, real dialog under I18nProvider + LocalizationProvider; expected strings from Intl). Merged origin/main once (merge e42274e0, after #26323 landed; main's plugin-grid.json taken, then re-recorded under the lock). #26354 had NOT merged when the PR opened (gh api pulls/26354 .merged = false at 23:16Z), so no second merge.",
      "files_changed": [
        "ui/packages/plugin-grid/src/components/BulkActionDialog.tsx",
        "ui/packages/plugin-grid/src/components/bulkConfirmText.ts",
        "ui/packages/plugin-grid/src/__tests__/bulkConfirmLocale-26307.test.tsx",
        "scripts/sdui-manifest.sources/plugin-grid.json",
        ".changeset/26307-bulk-confirm-locale.md"
      ],
      "line_budget": "146 changed lines at 64e9dbc5 vs merge base (dispatch-gates count before the matcher fix: +143/-3, 5 files); under the 3000 human-merge threshold",
      "tests": "All from cwd ui/, under bash scripts/pm/os-verify-lock.sh. (1) Pin red/green at head 64e9dbc5 (VERDICT command-exit 0): red leg = the two source files restored to a70aa00b (base versions) in the working tree only -> Tests 3 failed | 2 passed (5); failures: helper de-DE expected 'Set 1.234.567,50 € on 2 records?' received 'Set €1,234,567.50 on 2 records?'; helper zh-CN expected 'Due 2026年10月26日?' received 'Due October 26, 2026?'; dialog de-DE expected 'Set the amount to 1.234.567,50 € on 2 records?' received 'Set the amount to €1,234,567.50 on 2 records?'. Restore proven by HEAD blob hash match + empty git diff HEAD; green leg Tests 5 passed (5). (2) Full plugin-grid suite, pnpm exec vitest run packages/plugin-grid --maxWorkers=2, at e42274e0 (VERDICT command-exit 0 for the wrapper; SUITE_EXIT=1): Test Files 1 failed | 251 passed (252), Tests 1 failed | 2251 passed (2252); the one failure was this pin's dialog case using findByText, whose normaliser drops the de-DE no-break space before the euro sign in node text but not in the matcher; the DOM dump showed the correct German sentence. 64e9dbc5 changes only that matcher (reads the description textContent), and (1) is 5/5 at that head. The full suite was not re-run at 64e9dbc5 (declared narrowing: only the pin file changed, and test files are outside every other suite). (3) @object-ui/plugin-grid type-check (tsc --noEmit && tsc -p tsconfig.test.json): TC_EXIT=0 at e42274e0. Ablation: none beyond the red leg (the red leg is the source-level reversal of the fix; vitest resolves @object-ui/* from src, so no dist rebuild is involved).",
      "gates": "sdui: node scripts/gen-sdui-manifest-node.mjs under the lock at e42274e0 -> GEN_EXIT=0, '1 of 27 registry package record(s) re-recorded: plugin-grid', artefact unchanged (manifestSha256 1f6e1ccb925a…); node scripts/check-sdui-manifest.mjs -> SDUI_CHECK_EXIT=0. Derived: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 64e9dbc5 -> 45 commands; 44 exit 0; pnpm check:type-check-debt exit 3 (PREREQUISITE NOT MET: queued on the shared lock for its closure build) = NOT MEASURED locally, CI Lint & Repo Gates runs it. --ran reconciliation: '45 derived famil(ies) accounted for — 44 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)', 0 UNRUN. The derivation warned the tree is 5-7 commits behind origin/main (gate scripts changed across that range). CI check-runs on PR 26358: in_progress / not read (report delivered at local-verify end, per contract).",
      "dogfood_battery": "n/a — no ! in the PR title and Clause-②: no has no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2 relay strokes — (1) pr_create via scripts/pm/fleet-write/dispatch.mjs: POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/26358/assignees (os-steve); read-back identical 4726/4726 bytes; (2) this os-dev-report comment via post-stamped.mjs: POST /repos/objectstack-ai/objectstack/issues/26307/comments. git pushes (6) are not REST writes.",
      "deviations": [
        "First locked lap (rg5) ran on a fresh worktree whose @objectstack/spec dist was absent, so the pin could not load; I stopped my own run by recorded PIDs (the first recorded PID was the backgrounding subshell, so the wrapper and its vitest kept running until I stopped them by their PIDs). Its SUITE_EXIT=0 came from a vitest I had killed and is discarded. Its sdui re-record completed (GEN_EXIT=0, check exit 0) and was committed as ca5c898c, then superseded by the merge and the re-record at 92071423.",
        "My locked lap rg6/rg7 held the shared lock 1390s (23m10s): closure build + full plugin-grid suite + type-check + sdui re-record in one lap to avoid re-queueing. The wrapper flagged it as a long hold.",
        "Commit trailers are the model-free AGENTS.md pair (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By line, since the pre-push hook refuses a model identifier. The merge commit e42274e0 carries git's default message without trailers.",
        "Full plugin-grid suite measured at e42274e0, not at the final head 64e9dbc5 (only the pin file differs)."
      ],
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26307,
      "status": "done",
      "round": "patch — merge after #26354",
      "branch": "claude/issue-26307-bulk-confirm-locale",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26358",
      "head": "661393e6b2add11199010c2abec2205883c28601",
      "session": "session_01SMps7wq7rBTk53e8KTSEup",
      "premise_still_valid": true,
      "summary": "Merged origin/main after #26354 (merge a47d3696, main's plugin-grid.json taken byte-identical), re-recorded under the lock (661393e6: plugin-grid alone, artefact unchanged). No source change this round.",
      "files_changed": [
        "scripts/sdui-manifest.sources/plugin-grid.json (re-record)"
      ],
      "tests": "One locked lap at a47d3696 (VERDICT command-exit 0, held 1186s): closure build exit 0; pin bulkConfirmLocale-26307 Tests 5 passed (5); full plugin-grid suite Test Files 252 passed (252), Tests 2252 passed (2252); plugin-grid type-check exit 0. 661393e6 changes only the sources record.",
      "gates": "gen-sdui-manifest-node exit 0 ('1 of 26 registry package record(s) re-recorded: plugin-grid', manifest sha256 1f6e1ccb925a… unchanged); check-sdui-manifest exit 0. dispatch-gates --commands at 661393e6: 45 derived, 44 run each exit 0, pnpm check:type-check-debt NOT MEASURED (needs the shared lock for its closure build; CI Lint & Repo Gates runs it); --ran: '45 derived famil(ies) accounted for — 44 run, 1 NOT-MEASURED', 0 UNRUN. PR check-runs not read.",
      "dogfood_battery": "n/a — no ! in the title, Clause-②: no",
      "mcp_calls": "0",
      "api_writes": "1 — this comment (POST /repos/objectstack-ai/objectstack/issues/26307/comments via post-stamped.mjs relay). PR body NOT written this round.",
      "deviations": [
        "The seat order asked me to add the six-line 维护者速读 block to the PR body by relay. .claude/agents/os-dev.md line 63 (binding over the order) says the dev writes the PR body once, at pr_create, never PATCHes it, and the seat writes later changes. I did not PATCH; the block is below, ready to insert after the two declaration lines.",
        "Worktree /home/claude/objectstack-issue-26307 removed after the push (pushed and clean verified first)."
      ],
      "open_questions": [],
      "out_of_scope_findings": []
    }

    The 维护者速读 block for the seat to insert into the PR #26358 body, right after the Metadata-protocol: line:

    ## 维护者速读
    改了什么:批量操作的确认句(如「把金额设为 {{ amount | currency:EUR }},共 {{ count }} 条」)现在按查看者的显示语言格式化数字、金额和日期;德语用户看到 `1.234.567,50 €`,不再是 `€1,234,567.50`。
    为什么改:同一个操作的单条确认自 #26282 起已按查看者语言显示,批量确认却仍对所有人用 en-US;#26282 当时把这一半留给了 #26217,而后者合并时没带上。
    风险与回滚:只多传一个参数,读的是控制台已有的同一个 `useDisplayLocale()`,不新增语言判定;没有语言信息时仍是 en-US,原有英文句子不变。回滚 = revert 本 PR。
    先看:`ui/packages/plugin-grid/src/components/BulkActionDialog.tsx`(风险 hunk:确认步 `bulkConfirmText(…, displayLocale)` 一行)→ `ui/packages/plugin-grid/src/components/bulkConfirmText.ts`(第三参数直传 `fillTemplateHoles`)→ `ui/packages/plugin-grid/src/__tests__/bulkConfirmLocale-26307.test.tsx`。
    两分钟验证:在 `ui/` 下 `pnpm exec vitest run packages/plugin-grid/src/__tests__/bulkConfirmLocale-26307.test.tsx`,预期 `Tests 5 passed (5)`。
    需要你做的:无,席位落地。

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · seat 2 · 2026-10-27T00:23Z

    probe reading
    PR #26358 MERGED 2026-10-27T00:20Z, merge commit 6390fe52, head 661393e6 (+158/−3, seat record on #26358)
    origin/main contains it (git merge-base --is-ancestor exit 0)
    queue branch gh-readonly-queue/main/pr-26358-*: none left (consumed)
    card closed (completed) by Fixes #26307 on #26358

    Status: a bulk action's confirm sentence now formats its currency, number and date holes in the viewer's display locale, the same one the single-record confirm uses since #26282. pm:dispatched removed.

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 notificationsbugSomething isn't workingdomain:uipriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions