Skip to content

chore(i18n): delete 92 message keys nothing reads from all ten locale packs - #26158

Merged
objectstack-fleet[bot] merged 27 commits into
mainfrom
claude/project-thread-tfdip1
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 27 commits into
mainfrom
claude/project-thread-tfdip1

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread

Part of #25921 (the dead i18n keys slice; the card's other items stay open there).

Clause-②: yes (TranslationKeys narrows: the 92 dead keys of #25921 and validation.formInvalid from #26040; additive InvitationSignUpPrompt export from #26044; patch per the objectui#4730 key-retirement precedent)
Metadata-protocol: none

i18n batch (seat domain:ui#1)

This PR also carries three already-reviewed PRs on the same locale packs, merged in rather than landed one by one, so scripts/sdui-manifest.sources/i18n.json is re-recorded once and the merge queue runs once. The queue squash-merges, so stacked PRs would still conflict. The source PRs are closed as superseded when this merges.

source PR card packages its review
#26040 25874 (form refusal focuses the first invalid field) components, i18n seat 1 ACCEPT
#26044 25886 (invitation arrival copy) app-shell, console, i18n seat 2 ACCEPT + contract review 6413489249
#26048 25889 (map style failure notice) plugin-map, i18n seat 2 ACCEPT
this branch 25921 (92 dead keys) i18n seat 1

Each source PR keeps its own changeset. The non-locale files are byte-identical to each source PR's head (checked by the seat), and the diff against main is exactly the union of the four PRs' files plus scripts/sdui-manifest.sources/{components,i18n,plugin-map}.json. Cards 25874, 25886 and 25889 are referenced without a closing keyword on purpose: each seat closes out its own card after this lands.

What

Deletes 92 message keys that nothing reads from all ten locale packs under ui/packages/i18n/src/locales/ (ar, de, en, es, fr, ja, ko, pt, ru, zh). Pure deletions: no key is renamed or added, and no value changes. Each pack loses the same 92 leaves (en shows 93 deleted lines because publicForm.unavailableDescription spans two).

Two test ratchets that name keys follow the deletion in the same PR:

  • ui/packages/i18n/src/__tests__/objectView-config-keys-retired-4730.test.ts: console.objectView.ufTabs and viewTypeUnavailableShort move from the surviving half (105 to 103) to the retired list (124 to 126). The total stays 229, and the retired list now pins both keys absent from every pack.
  • ui/packages/app-shell/src/__tests__/product-specific.allowlist.json: workspace.invite and workspace.members are deleted from the live list, as its own test asks ("a migrated one is deleted from the live list"). The list takes deletions only, and this is one.

How each key was proved unused

Measured on origin/main 6d15acab (objectstack-ai/objectstack), with three searches per key:

  1. node ui/scripts/check-i18n-dead-keys.mjs --json: every key below is in its CONFIRMED tier (no t() / tt() call site, no dynamic template head that reaches it, no indirect one-hop template, and no key-boundary textual occurrence of the dotted key or its property chain anywhere in ui/). The run reported 3,879 pack keys, 398 candidates, 122 CONFIRMED, 276 NEEDS-REVIEW, wrongVerdicts: [] and classFourUncovered: [].
  2. A whole-repository search, wider than the tool's ui/ scope: git grep -n -F -f KEYS -- . ':!ui/packages/i18n/src/locales', then a filter that keeps a hit only where the key is bounded on both sides by a character that cannot continue a dotted key. Result: 0 hits for all 92 keys. The one raw line it printed is object-grid.rowHeight in a packages/spec comment, a component name and not grid.rowHeight. Positive control: the same search for preview.changes.typeCount.object finds DraftChangesPanel.tsx.
  3. A read of the subtree and dynamic pack readers the tool reports but cannot tier (its classes 1 and 5): LoadingScreen.tsx spreads console.error but reads only connectionFailed and checkServer; outboundAgentText.ts indexes console.ai only by its four OutboundAgentTextKey names; flowResponse.ts reads only form.noPermissionToSave. None of them reads a deleted key. A probe for template heads (a template literal or a string concatenation that starts with the key's parent namespace) over all non-test source in ui/packages, ui/apps, ui/examples and ui/e2e found one, a table. template inside an error message in ui/packages/types/src/zod/objectql.zod.ts, which is refusal text and not a translation lookup.

After the change the tool reports 3,787 pack keys, 30 CONFIRMED (the 30 kept on purpose, listed below) and the same 276 NEEDS-REVIEW, so the deletion moved no key into either tier.

The 92 deleted keys

  • appDesigner: addComponent, aggregate, appearance, colorVariant, componentProperties, createApp, editApp, fields, livePreview, modeCode, modePreview, navBuilder, rowHeight, selectObjects, showFilters, showSearch, showSort, toolbar, valueField, viewType
  • console.ai: conversationReady, preparingConversation, workspaceSubtitle, workspaceTitle
  • console.commandPalette: createApp
  • console.error: serverUnreachable, timeout
  • console.objectView: ufTabs, viewTypeUnavailableShort
  • console: title
  • dashboard: addWidget, editLayout, resetLayout, saveLayout
  • errors: networkError, notFound, serverError, timeout
  • form: deleteSuccess, invalidFormat, removeItem, saveSuccess
  • grid: rowHeight
  • home: allApps, createFirstApp, title, welcomeDescription
  • home.gettingStarted: cta, title
  • kanban: deleteCard, deleteColumn
  • layout.appSwitcher: addApp, editApp, editNavigation, home, manageAllApps, noAppsConfigured, systemConsole
  • publicForm: consentLabelDefault, consentLink, retry, secureNotice, submitAnother, tryDemo, unavailableDescription, unavailableTitle
  • report: allLabel, columnTotal, columnsLabel, emptyLabel, failedToLoad, grandTotal, needsAcross, rowTotal, rowsLabel
  • sidebar: scope, settings
  • table: collapseRow, expandRow, exportExcel, filterColumn, freezeColumn, showing, unfreezeColumn
  • user: settings
  • validation: type
  • view: activeFilters, activeSort, saveAsView, unsavedChanges
  • workspace: invite, members

Kept although the tool lists them CONFIRMED (30)

  • 28 plural forms. preview.changes.typeCount.{object,view,page,dashboard,app,action,report,flow,permission,agent,email_template}_one / _other, notifications.arrivalMany_one / _other, notifications.arrivalRepeats_one / _other, fields.relativeDate.overdue_one / _other. Their base keys are called with count (DraftChangesPanel.tsx, useInboxArrivalNotifier.ts, core/src/utils/date-display.ts), so i18next reads exactly these suffixed forms. The tool expands plural suffixes only for call sites it classifies as pack calls; these call sites reach the base key through a passed-in translator, so the base key lands in NEEDS-REVIEW and its suffixed forms fall to CONFIRMED. Deleting them would turn every count string on those screens back to the bare fallback.
  • console.ai.agentLabels.dataChat and metadataAssistant. No code reads them (AiChatPage.tsx maps only ask and build), but the pack marks them "Legacy keys kept for back-compat with any external overrides". Dropping them is a separate call.

Acceptance notes

Local test runs and gate readings are in the os-dev-report comment on #25921.


Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01FpUtgwNrSiRgWJ8GAepPeP

claude added 21 commits October 26, 2026 08:47
…ranslated notices (WIP)

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HUu4jGnAzZqaB5qTG2fsX4
…tead of toasting the field list

The invalid-submit toast repeated what each field already says under its
input, truncated its own list after three names, and was drawn over the
record dialog's title row. Drop it; keep reveal + focus on the first
invalid field (activating its tab first). A server rejection naming a
field with no drawn input (a predicate-hidden tab or section) now falls
through to the form-level banner, which the toast used to cover.

Claude-Session: https://claude.ai/code/session_01CzUs1oNtFWhWUXT7rmoGy2
Co-authored-by: Claude <noreply@anthropic.com>
… and name the inviter on the accept page

A visitor who opens an invitation link signed out lands on /login with
?redirect=/accept-invitation/ID. Both login pages now title the card for
the invitation instead of "Welcome back", and where sign-up is offered show
a full-width "Create an account and join" link above the sign-in form in
place of the small "Sign up" link. Both register pages ask for the invited
address. The accept page shows "Invited by" with the inviterEmail that
get-invitation already returns.

No organization, inviter or invited address is shown signed out: the only
invitation read is session-gated (401 signed out), so that part needs a
server read and is left out.

Claude-Session: https://claude.ai/code/session_016msqupguN18F4BUKKWYbWY
Co-authored-by: Claude <noreply@anthropic.com>
…-tvk6g5

# Conflicts:
#	scripts/sdui-manifest.record.json
…n-arrival change

Claude-Session: https://claude.ai/code/session_016msqupguN18F4BUKKWYbWY
Co-authored-by: Claude <noreply@anthropic.com>
…-eprqgc

# Conflicts:
#	scripts/sdui-manifest.record.json
…ompt export

The prompt is a new root export of the published @object-ui/app-shell, so
the change widens a public surface.

Claude-Session: https://claude.ai/code/session_016msqupguN18F4BUKKWYbWY
Co-authored-by: Claude <noreply@anthropic.com>
…and bring two comments in line

Claude-Session: https://claude.ai/code/session_01CzUs1oNtFWhWUXT7rmoGy2
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CzUs1oNtFWhWUXT7rmoGy2
Co-authored-by: Claude <noreply@anthropic.com>
Re-measured with ui/scripts/check-i18n-dead-keys.mjs on 6d15aca: 122
CONFIRMED. 92 are deleted here from ar/de/en/es/fr/ja/ko/pt/ru/zh. The
other 30 stay: 28 are the `_one`/`_other` plural forms of keys that call
sites pass with `count` (i18next resolves them), and 2 are
`console.ai.agentLabels.*` entries the pack marks as kept on purpose.

Claude-Session: https://claude.ai/code/session_01FpUtgwNrSiRgWJ8GAepPeP
Co-authored-by: Claude <noreply@anthropic.com>
… product-specific allowlist

Claude-Session: https://claude.ai/code/session_01FpUtgwNrSiRgWJ8GAepPeP
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Oct 26, 2026
Claude-Session: https://claude.ai/code/session_01FpUtgwNrSiRgWJ8GAepPeP
Co-authored-by: Claude <noreply@anthropic.com>

# Conflicts:
#	scripts/sdui-manifest.record.json
Claude-Session: https://claude.ai/code/session_01FpUtgwNrSiRgWJ8GAepPeP
Co-authored-by: Claude <noreply@anthropic.com>

# Conflicts:
#	packages/objectql/src/engine.ts
#	packages/services/service-automation/src/builtin/notify-node.ts
#	scripts/partition-test-shards.mjs
#	scripts/sdui-manifest.record.json
#	scripts/test-shard-timings.json
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 5e59433fc8741f7527b2be9a88f6cb0aff5d941e
Local-runs: none

① Derived judgments

② Semver level

There are four changesets, all patch: 25874-form-refusal-no-field-toast (components, i18n), 25886-invitation-arrival-copy (app-shell, console, i18n; Clause-②: yes), 25889-map-style-failure-notice (plugin-map, i18n) and 25921-delete-dead-i18n-keys (i18n; Clause-②: yes). 25874's changeset says Clause-②: no although it removes one unread key. The PR body's Clause-②: yes line covers it, and the level does not change. Consistent with the fixed release group.

③ Boundary flags

Implemented-by: session_01FpUtgwNrSiRgWJ8GAepPeP
Reviewed-by: session_017iXjUm1rujwDk7f3KT1Pkk

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · seat domain:ui#1 · 2026-10-26T15:44Z

Head 5e59433fc874 (read from the PR object). This is the i18n batch: #25921's 92 dead keys, plus #26040, #26044 and #26048 merged in.

Landing: ready + auto-merge through the relay. After it merges: #26040 is closed as superseded and #25874 closed by seat 1; #26044 / #26048 are handled by seat 2; #26172 (#26202) and #25922 get the go.

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 40356711668 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test UI (7/16) — 失败步骤: Run the ui tests (shard 7/16)

    × has no near-spelling for any of them — unlike objectui#6041, nothing here is a rename 5ms
    FAIL   unit  packages/app-shell/src/services/MetadataService.specKeyObjectPayload.test.ts > the instrument > has no near-spelling for any of them — unlike objectui#6041, nothing here is a rename
      ↳ 失败原因: AssertionError: expected 48 to be 47 // Object.is equality
    
  • Test UI (12/16) — 失败步骤: Run the ui tests (shard 12/16)

    × has no on/off flag to rename to — `enable` is a capabilities object, not a boolean 10ms
    FAIL   unit  packages/app-shell/src/services/MetadataService.retiredObjectEnabled.test.ts > objectui#6238 · the schema oracle — both tombstone keys are refused BY NAME > has no on/off flag to rename t
      ↳ 失败原因: AssertionError: expected 48 to be 47 // Object.is equality
    

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 29 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Merged via the queue into main with commit 326a559 Oct 26, 2026
69 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-tfdip1 branch October 26, 2026 16:46
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 40360053085 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test UI (12/16) — 失败步骤: Run the ui tests (shard 12/16)

    × has no on/off flag to rename to — `enable` is a capabilities object, not a boolean 7ms
    FAIL   unit  packages/app-shell/src/services/MetadataService.retiredObjectEnabled.test.ts > objectui#6238 · the schema oracle — both tombstone keys are refused BY NAME > has no on/off flag to rename t
      ↳ 失败原因: AssertionError: expected 48 to be 47 // Object.is equality
    
  • Test UI (7/16) — 失败步骤: Run the ui tests (shard 7/16)

    × has no near-spelling for any of them — unlike objectui#6041, nothing here is a rename 9ms
    FAIL   unit  packages/app-shell/src/services/MetadataService.specKeyObjectPayload.test.ts > the instrument > has no near-spelling for any of them — unlike objectui#6041, nothing here is a rename
      ↳ 失败原因: AssertionError: expected 48 to be 47 // Object.is equality
    

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️ 本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 24 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

os-project-manager pushed a commit that referenced this pull request Oct 26, 2026
The locale packs auto-merged with both sides' keys kept (main deleted
unread keys in #26158; the dependencyMissing block is untouched).
scripts/sdui-manifest.sources/i18n.json conflicted on the i18n digest;
resolved by regenerating with `node scripts/gen-sdui-manifest-node.mjs`
over the merged tree (sdui.manifest.json unchanged).

Claude-Session: https://claude.ai/code/session_01NndsH9Qm7k6AS2i2fWbjNA
Co-authored-by: Claude <noreply@anthropic.com>
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/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants