Uh oh!
There was an error while loading. Please reload this page.
refactor(app-shell): 删除 RecordFormPage 中不可达的 i18n defaultValue 兜底 - #3516
Merged
Conversation
…ordFormPage
The page handed i18next an inline `defaultValue` on eight `t()` calls. Seven of
those branches were unreachable: their keys are defined in all ten locale packs
and `all-locales-key-parity.test.ts` pins that permanently, so i18next always
resolved the pack value and the fallback could never render.
What the dead fallbacks did do is carry a second, unwatched English spelling of
the same string. `form.createTitle`'s default read `New {label}` while the pack
says `Create {{object}}` — a different verb for one title at one call site. Had
the key ever been renamed or dropped, the title would have silently changed
from "Create Contacts" to "New Contacts" with every test still green. Deleting
them makes "this title has exactly one English spelling" a structural fact
rather than a coincidence, consistent with the declared=enforced precedent in
objectui#3470/#3483.
`form.createTargetOrg` is KEPT and documented: it is defined in no pack, not
even `en`, so its `defaultValue` is genuinely what renders.
Rendered copy is unchanged in every locale — verified by restoring the deleted
limbs on top of the new test file, which stays green (a dead branch cannot move
an assertion), and by a counterfactual: with `form.createTitle` removed from
`en`, the old code silently rendered "New Contacts" while the new code renders
the raw key and the new pack-coverage test fails loudly.
Fixes#3469
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRtThe latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
marked this pull request as ready for review
August 7, 2026 02:13
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 7, 2026
akarma-synetal pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Aug 10, 2026
…locales (objectstack-ai#3526) `form.createTargetOrg` was defined in no locale pack — not even `en` — so i18next genuinely missed it and `RecordFormPage`'s inline `defaultValue` rendered instead. The ADR-0105 badge therefore read English `Creates in <org>` in all ten languages (a Chinese console creating a record on an org-walled object showed `Creates in 某某组织`). `all-locales-key-parity.test.ts` could not see this: it asserts every pack defines every `en` key, so a key `en` itself lacks is outside the comparison, and ten packs missing it identically kept parity green. Backfill `createTargetOrg: 'Creates in {{org}}'` into `en` (the parity gate then demands it from the other nine, each translated to its pack's existing form tone), delete the inline default, and invert the two exception pins objectstack-ai#3516 left: the key joins BARE_KEYS, and the render assertion checks the badge against the pack copy in both `en` and `zh`. Fixesobjectstack-ai#3517 Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Aug 10, 2026
…veness ratchet (objectstack-ai#3601) (objectstack-ai#3623) The deny-list guards objectstack-ai#2561 decision (a): spec/ui zod validators are not part of @object-ui/types' public surface. A row only asserts something while the spec still publishes the name — once upstream retires it, nothing can re-export it and the row passes as a tautology. 37 of 82 rows had rotted that way: objectstack#4988 / PR #5321 (32, the five interaction-config modules), objectstack-ai#4610 (2), objectstack-ai#3494 / PR objectstack-ai#3516 (2), objectstack-ai#3896 (1). They are deleted; the 45 rows that still guard a live spec export are untouched. The new ratchet asserts every remaining row resolves to a real `@objectstack/spec/ui` export (with the Spec-prefix alias fallback the list's own doc comment describes, load-bearing for 16 of the 45), so the next upstream retirement fails here by name instead of waiting for an audit. Same technique as PR objectstack-ai#3568's `$icontains` exclusion ratchet. Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Aug 10, 2026
…t path filtering into the jobs (objectstack-ai#3523 steps 1-2) (objectstack-ai#3722) * ci(step 1): subscribe ci/lint/control-bytes/docs-links to merge_group (objectstack-ai#3523) Step 1 of objectui#3523, on its own commit as the issue's ruling requires: this is a pure addition and changes nothing about which pull requests or pushes run CI. The merge queue is enforced on this repository by a ruleset (objectstack-ai#3243 measured a direct push to `main` returning 405 `Changes must be made through the merge queue`), but not one of the 22 workflows subscribed `merge_group` — repo-wide `event=merge_group` runs stood at total_count = 0. A queue nothing subscribes to can only carry an empty required-check set, so it rebuilt each PR on the current `main` and let it through without validating anything. On 2026-08-07 that was cashed in: objectstack-ai#3503 / objectstack-ai#3510 / objectstack-ai#3516 merged between 02:11Z and 02:15Z with `Type Check` at conclusion=failure, on a `main` poisoned by objectstack-ai#3498, and objectstack-ai#3505 had to hot-fix it. The four workflows named by the issue now carry the trigger. It is spelled `merge_group: types: [checks_requested]`; `checks_requested` is the only activity type GitHub defines for this event today, so this is equivalent to objectstack's bare `merge_group:` and merely refuses to inherit a second type added later. Two `ci.yml` jobs additionally needed to be told the third event exists — both changes are no-ops for `pull_request` and `push`: - `test` moves from `if: github.event_name == 'pull_request'` to `!= 'push'`, so a queue build actually runs the suite instead of skipping every shard. - `docs`'s `should_run` treated anything that is not `push` as a pull request and diffed `github.event.pull_request.*`, which is null on a queue build — an empty revision range, i.e. the site build silently skipped on the last check before `main`. Verified rather than assumed: `concurrency` needs no merge-queue case. On `merge_group` the group expression falls back to `github.ref`, which is the queue's own generation — measured on objectstack, whose 3552 queue runs report head_branch `gh-readonly-queue/main/pr-6594-251e888a…`. That can collide with neither a PR group (a bare number) nor a push group (`refs/heads/main`). Refs objectstack-ai#3523 * ci(step 2): move ci/lint path filtering out of the PR trigger into the jobs (objectstack-ai#3523) Step 2 of objectui#3523, deliberately a separate commit from step 1: step 1 adds runs that did not exist, this one changes which pull requests start CI, and mixing them would make that impossible to review apart. `on.pull_request.paths-ignore` skips the WHOLE workflow when every changed file matches, and GitHub has no per-job path filter. A docs-only or changeset-only PR therefore started neither `ci.yml` nor `lint.yml` — objectstack-ai#3509 measured zero check runs from them. A check that is never *created* does not fail a required-status-check rule, it leaves the pull request pending; inside the merge queue it fails on the ruleset's 60-minute status-check timeout. So none of `Lint`, `Type Check`, `Test (shard N/4)`, `Build & E2E` or `Changeset Fixed Group Check` could be made required while the filter lived on the trigger — which is why the queue's required set was empty to begin with. The filter is not deleted, it moves. `type-check`, `test`, `e2e` (ci.yml) and `lint` (lint.yml) each open with a `Decide whether this change needs a full run` step, and every step after it carries `if: steps.relevant.outputs.should_run == 'true'`. The job always runs and always reports; the paths decide only whether it does any work. That is the shape `ci.yml`'s `docs` job has used since objectstack-ai#3450 — not a new mechanism — and the exclusion lists are byte-for-byte the `paths-ignore` they replace, so which PRs pay for a full run is exactly as before. Two deliberate narrowings, both stated so they can be argued with: - `paths-ignore` stays on the `push` trigger. Branch protection and the merge queue judge pull requests and queue builds, never pushes to `main`, so the push lane gains nothing from losing it and would cost a full CI run on every docs merge. It also gives the ignore list one authored home, which `merge-queue-reporting.test.ts` then pins the in-job copies against. - `changeset-check` is not gated. It is a checkout and one `node` call, so short-circuiting it would cost more in complexity than it saves. The gate fails OPEN: an unresolvable diff runs everything rather than reporting green having built nothing (objectstack#4928's filter contract). Measured both ways against a fixture repository — as shipped an unreachable base sha yields should_run=true; rewritten with the `|| echo ""` spelling `ci.yml`'s older `docs` gate still uses, the same input yields should_run=false, i.e. a silent full skip. That pre-existing `docs` gate is reported separately rather than changed here. `scripts/__tests__/merge-queue-reporting.test.ts` pins both steps; against the pre-change workflows 9 of its 10 assertions go red, each naming its own regression. `lint-workflow.test.ts`'s TypeScript-exclusion tripwire now reads the in-job list as well as the trigger, which is where that list now lives. Refs objectstack-ai#3523 --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#3469
采用 issue 分诊结论的 B 向:删掉死的
defaultValue分支,而不是把它改成和语言包一致的拼法。与 #3470/#3483 的「declared = enforced、消费端不留容忍」先例一致。改了什么
packages/app-shell/src/views/RecordFormPage.tsx原本在 8 处t()调用上挂了内联defaultValue。逐一评估后:form.createTitleNew ${label},包里是Create {{object}})form.editTitleform.createSuccessform.updateSuccessform.saveRecordcommon.backcommon.cancelform.createTargetOrgen都没有删掉的七条,key 由
all-locales-key-parity.test.ts永久钉住必然存在,i18next 永远解析得到包里的值,兜底分支不可能被渲染。它们真正的作用是给同一条文案带了第二处没人看管的英文拼法:form.createTitle的默认值写的是New ${label},包里却是Create {{object}},同一个标题、同一个调用点、两个动词。哪天这个 key 被改名或删掉,页面标题就会从Create Contacts悄悄变成New Contacts,而且没有任何测试会红。保留的那一条已在代码里写清原委:
form.createTargetOrg在十个包里都没有定义,i18next 是真的 miss,兜底就是实际渲染出来的东西 —— 删了会把裸 key 摆到页面上。这也是该徽标在十种语言下都是英文的原因。把 key 回填进en(parity 测试会带着其余九个包跟上)才是正解,届时在同一个改动里删掉这条兜底,新测试里有一条断言就是提醒这件事的。行为不变的证据(以及反向验证的真实方向)
「把删掉的分支放回去看测试变红」在这个改动上从构造上就不可能成立,预测在跑之前就已写进测试文件头:key 在包里,i18next 根本不会去看
defaultValue,所以把defaultValue加回来,断言一条都不会动。这正是 issue 报告的那个缺陷本身 —— 一段没有任何测试能看见的死分支。所以这里如实记录三个方向的实测:git checkout origin/main -- RecordFormPage.tsx把八处defaultValue全部放回,新测试文件 16/16 依旧全绿。form.createTitle从en包里删掉,旧代码渲染出PROBE_TITLE=[New Contacts]—— 动词静默改变,没有任何测试信号。PROBE_TITLE=[form.createTitle](裸 key + dev missing-key 告警),且新增的覆盖率断言直接变红:form.createTitle missing from: en。反事实用的语言包临时改动是一次性探针,已还原,未进入本 PR(
git status已核验)。新增测试
packages/app-shell/src/views/RecordFormPage.i18n.test.tsx(16 个用例),钉住删除之后真正成立的不变式:en和zh两种语言下各断言一次 —— 任何硬编码的英文默认值都无法同时满足这两条(zh下标题是新建Contacts)。New Contacts不在页面上。form.createTargetOrg的两条:一条钉住它当前在十个包里都不存在(所以兜底必须留),一条钉住徽标确实渲染出Creates in Acme Inc而不是裸 key。验证
pnpm exec vitest run packages/app-shell/src/views/RecordFormPage.i18n.test.tsx packages/i18n/src/__tests__/all-locales-key-parity.test.ts→ 36 passedpnpm exec vitest run packages/app-shell/src/views(消费半径清扫)→ 180 files / 1585 passedpnpm exec turbo run type-check --filter=@object-ui/app-shell→ 通过pnpm exec eslint两个改动文件 → 0 errornode scripts/check-control-bytes.mjs/check-changeset-no-major.mjs→ 通过顺带发现(未在本 PR 修改)
form.createTargetOrg被组织租户徽标消费,却在十个语言包里全部缺失 —— 属于「组件引用了不存在的 key」这一类漂移,与本 issue 是两件事,已另行记录待 PM 分诊。🤖 Generated with Claude Code
https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
Generated by Claude Code