Skip to content

Commit 00dde01

Browse files
test(i18n): derive console.objectView survivors from en instead of pinning counts (#26510)
<!-- claude-projects-attribution: {"github_login":"os-steve"} --> _Requested by **Steve** · [project thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9UqY9fBL2b73xq6oRApRHbw)_ Fixes #26438 Part of #26431 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:`objectView-config-keys-retired-4730.test.ts` 去掉三个手打计数(存活键 103、合计 229、退役清单长度 126),存活键集合改为从 en 包派生。 - 为什么改:每往 `console.objectView` 加一个正常的新键,这个测试就红,只能改数字;它要守的只是「退役键不回来」。 - 风险与回滚:只改一个测试文件,不发布任何东西;回滚就是 revert 本 PR。 - 先看:`ui/packages/i18n/src/__tests__/objectView-config-keys-retired-4730.test.ts` 第一个 `it`(计数换成去重 + 非空检查)和第三个 `it` 开头(`toHaveLength(SURVIVING_KEY_COUNT)` 换成非空检查)。 - 两分钟验证:`pnpm --filter @object-ui/i18n exec vitest run --root ../.. packages/i18n/src/__tests__/objectView-config-keys-retired-4730.test.ts`(在 `ui/` 下),预期 5/5 通过。 - 需要你做的:无,席位落地。 ## What changes One test file, test-only (`skip-changeset`: nothing under any package's `files[]` moves). - **Deleted** `SURVIVING_KEY_COUNT = 103`, the `RETIRED + SURVIVING == 229` sum, `enKeys toHaveLength(SURVIVING_KEY_COUNT)` and the retired list's own `toHaveLength(126)`. - **Kept, unchanged:** `RETIRED_OBJECT_VIEW_KEYS` (every name), the assertion that no pack defines any retired key (named pack and key in the failure), the assertion that every pack holds exactly `en`'s `console.objectView` key set (local CLDR plural slots excepted, as before), the live-sample translation check, and the `t()` reader scan. - **Replaced with anti-vacuity:** the retired list is non-empty and has no duplicate name (`new Set(list).size === list.length`); the `en` root is non-empty; `LANGS` contains `en`. - **Comments:** the arithmetic history (116 → 118 → … → 126 retired, 209 → … → 229 total, 104 → 105 → 103 surviving) is removed; each later retirement round keeps its provenance paragraph, retitled by key name instead of by count. No forced test edits: the only test touched is the one this card names. ## Why The file exists to prove that the objectui#4730 config-panel vocabulary never comes back. That needs the retired NAMES and a live-set comparison against `en`; a hand-typed surviving count adds nothing to it and reds on every legitimate addition (objectui#13052 and objectui#13330 each had to bump it). The retired list's length pin was the same transcription one level over: the names already pin the list, so the count only made each retirement a two-place edit. Card acceptance, measured below: adding a `console.objectView.*` key changes no test; retiring one still requires the list edit. ## Tests All runs at head `3ce8863d`, under `scripts/pm/os-verify-lock.sh`. - Target file: `Test Files 1 passed (1)`, `Tests 5 passed (5)`. - `pnpm --filter @object-ui/i18n test --maxWorkers=2`: `Test Files 87 passed (87)`, `Tests 1353 passed | 13 skipped (1366)`. - `pnpm --filter @object-ui/i18n type-check` (`tsc --noEmit && tsc -p tsconfig.test.json`): exit 0; `--listFiles` on `tsconfig.test.json` lists this test file (1 hit). ### Ablation Each leg mutates a locale pack through `node scripts/ablation-replace.mjs` (anchor must hit once, blob must change, restore proven by blob == HEAD and an empty `git diff HEAD`), inside a script with an EXIT/INT/TERM trap. 12 of 12 restores reported `ok restored`; `git status --porcelain` empty afterwards. The locale packs are not in this diff. | Leg | Mutation | Result | |:---|:---|:---| | A | retired `groupBy` put back into `en`'s `console.objectView` | **red**: 2 failed / 3 passed. `no pack defines any retired key` names `en :: console.objectView.groupBy`; the shape test reds too (9 packs now lack an `en` key) | | B | retired `groupBy` put back into `zh` only | **red**: 2 failed / 3 passed. Names `zh :: console.objectView.groupBy`; the shape test reds on `zh` | | C1 | a new key `ablationNewKey26438` added to all ten packs, this PR's test | **green**: 5/5 | | C2 | same mutation, `main`'s test (byte copy of `00a4593a`'s file) | **red**: 1 failed / 4 passed, `expected [ 'ablationNewKey26438', …(103) ] to have a length of 103 but got 104` | C1 against C2 is the defect the card names, before and after. ## Acceptance notes - `expect(LANGS).toHaveLength(10)` stays. It pins the locale roster, not this namespace, and the same line sits in eight sibling i18n test files plus `entry-locale-shape-7479.test.ts`; retiring it in one file alone would leave the roster pinned everywhere else. It belongs to the roster's own entry in the #26431 sweep if that sweep wants it. - The shape test still reds when a key is added to `en` alone. That is the parity rule (`all-locales-key-parity.test.ts` holds it repo-wide), not a count; leg C adds the key to every pack, which is what a legitimate addition does. ## Verification `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at `3ce8863d` derived 24 commands; all 24 ran with exit 0, reconciled with `--ran`: `24 derived famil(ies) accounted for — 24 run, 0 NOT-MEASURED (a DERIVED zero — all 24 recorded an exit code and none of them is 3)`. `pnpm --dir ui exec vitest run scripts/` first exited 1 on a missing prerequisite (`Cannot find package '@objectstack/spec/ui'`, spec not built in this fresh container). After `turbo run build --filter='@object-ui/i18n^...' --filter=@objectstack/spec` it ran green: `Test Files 178 passed | 2 skipped (180)`, `Tests 5097 passed | 2 skipped (5099)`. The type-check lanes, artifact rosters and wide-population families that the derivation prints outside its total are left to CI. ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. (not touched) - [x] 2. Every name I added uses the casing for its kind. - [x] 3. No formatter ran; `pnpm lint` passes. (left to CI's `Lint UI`) - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. (publishes nothing: `skip-changeset`) - [x] 6. `packages/spec` touched: its generated artifacts are regenerated and committed. (not touched) - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English. - [x] The *Before Submitting* list in CONTRIBUTING.md is done. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_01ADHkmQkvjvcnMCSgLomdvg)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7234f33 commit 00dde01

1 file changed

Lines changed: 46 additions & 65 deletions

File tree

‎ui/packages/i18n/src/__tests__/objectView-config-keys-retired-4730.test.ts‎

Lines changed: 46 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -91,21 +91,25 @@ const at = (pack: unknown, path: string): unknown =>
9191
path.split('.').reduce<unknown>((n, k) => (n as Record<string, unknown> | undefined)?.[k], pack);
9292

9393
/**
94-
* The 116 retired keys, listed BY NAME in the packs' own order rather than
94+
* The retired keys, listed BY NAME in the packs' own order rather than
9595
* counted. A count passes when one key returns and another leaves; the names do
96-
* not. 209 keys existed in the namespace; 116 were retired; 93 survived — 94
97-
* since objectui#5232 added `viewConfigPermissionDenied`, the refusal shown when
98-
* a session without `manage_metadata` tries to change ORG-WIDE view config
99-
* (objectstack#7494's ruling). An ADDITION to a live namespace is not a
100-
* regression of the retirement this file pins: none of the retired keys
101-
* came back, which is what the `it` below actually asserts.
96+
* not. objectui#4730 retired the first 116; the later rounds are named beside
97+
* their entries below.
10298
*
103-
* ## The objectui#8754 addition — 116 retired become 118
99+
* This list is the ONLY hand-maintained set in the file. The surviving half of
100+
* the namespace is not counted anywhere: it is a live namespace, and an ADDITION
101+
* to it is not a regression of the retirement this file pins. What the file
102+
* asserts about the survivors is derived from the `en` pack — every pack holds
103+
* exactly `en`'s key set — so adding a key to every pack changes no test here,
104+
* while retiring one still takes an edit to this list (nothing else in the repo
105+
* guards the key's absence afterwards; see the header).
106+
*
107+
* ## The objectui#8754 addition — `groupBy` and `toolbar`
104108
*
105109
* `groupBy` and `toolbar` joined the retired list in objectui#8754's deletion
106110
* round, and they are here rather than in that card's own pin because THIS file
107-
* owns the namespace's arithmetic: a `console.objectView.*` key retired anywhere
108-
* else would move the surviving count out from under the assertions below.
111+
* owns the namespace's retired list: a `console.objectView.*` key retired
112+
* anywhere else belongs in this one list, not in a second one beside it.
109113
*
110114
* Both were dead all along and were held out of objectui#4730's sweep by a
111115
* measurement defect, not by a reader. `check-i18n-dead-keys`' text net matched
@@ -117,21 +121,12 @@ const at = (pack: unknown, path: string): unknown =>
117121
* `toolbar` was not — a split with no reason behind it, which is the shape a
118122
* substring false-positive leaves.
119123
*
120-
* ⚠️ The total below is 225 and that is deliberate: 225 is the count of keys
121-
* that ever EXISTED in this namespace, split into a retired half that only
122-
* grows and a surviving half that shrinks to match. A retirement moves the
123-
* split; only a genuinely new key moves the total (objectui#10636's
124-
* `recordCountOne` moved it from 210; objectui#11445 retired that key and
125-
* added six count-family slots, `_one` / `_other` on `recordCount`,
126-
* `importedToast` and `bulkDeleteConfirm`, making 217; objectui#11576 added
127-
* eight dataset-binding keys, making 225).
128-
*
129-
* ## The objectui#11445 change — 118 retired become 119
124+
* ## The objectui#11445 change — `recordCountOne`
130125
*
131126
* `recordCountOne` was the singular half of a code-selected pair; the footer
132127
* now asks the `recordCount` count family, so the sibling left all ten packs.
133128
*
134-
* ## The objectui#11576 change — 119 retired become 123, 217 total become 225
129+
* ## The objectui#11576 change — the four chart-axis keys
135130
*
136131
* The create-view dialog's chart picks were the pre-ADR-0021 inline axes, which
137132
* the spec's list chart block refuses by name. The dialog now binds a dataset
@@ -141,14 +136,16 @@ const at = (pack: unknown, path: string): unknown =>
141136
* `datasetHelp`, `chartMeasure`, `chartMeasureHelp`, `chartDimension`,
142137
* `chartDimensionHelp`, `viewTypeUnavailableDataset`, `noDatasetMeasure`).
143138
*
144-
* ## The objectui#12504 change — 123 retired become 124
139+
* ## The objectui#12504 change — `viewConfigPermissionDenied`
145140
*
146-
* `viewConfigPermissionDenied` (objectui#5232's addition, above) was the toast
147-
* the console's toolbar write raised when the adapter's capability gate refused
148-
* an org-wide view-config change. The maintainer's ruling on objectui#12504
149-
* retired that write (`persistViewPatch`) with the adapter method and its gate
141+
* `viewConfigPermissionDenied` (objectui#5232's addition: the refusal shown when
142+
* a session without `manage_metadata` tries to change ORG-WIDE view config,
143+
* objectstack#7494's ruling) was the toast the console's toolbar write raised
144+
* when the adapter's capability gate refused an org-wide view-config change.
145+
* The maintainer's ruling on objectui#12504 retired that write
146+
* (`persistViewPatch`) with the adapter method and its gate
150147
* (`updateViewConfig`), and the toast was its only producer, so the key left
151-
* all ten packs. A retirement moves the split, not the total.
148+
* all ten packs.
152149
*/
153150
const RETIRED_OBJECT_VIEW_KEYS = [
154151
'accessibility',
@@ -287,43 +284,20 @@ const RETIRED_OBJECT_VIEW_KEYS = [
287284
'viewTypeUnavailableShort',
288285
] as const;
289286

290-
/**
291-
* How many keys the namespace keeps — the live + indirect-reference remainder.
292-
* 104 since objectui#13052 added `objectPackageDisabled`, the object route's
293-
* "this object's package is disabled" sentence (`ObjectNotFoundState`); 105
294-
* since objectui#13330 added `managedUsers`, Setup → Users' invite line and
295-
* 「未授权」 marker on a cloud-managed environment (`managedEnvironmentUsers`);
296-
* 103 since objectstack#25921 retired `ufTabs` and `viewTypeUnavailableShort`.
297-
*/
298-
const SURVIVING_KEY_COUNT = 103;
299-
300287
describe('console.objectView config-panel keys are retired (objectui#4730)', () => {
301-
it('the retired list is the measured set', () => {
302-
// Guards the premise the rest of the file rests on. If this arithmetic ever
303-
// stops holding, the assertions below are checking a set nobody chose.
304-
expect(RETIRED_OBJECT_VIEW_KEYS).toHaveLength(126);
305-
expect(new Set(RETIRED_OBJECT_VIEW_KEYS).size).toBe(126);
306-
// 209 at objectui#4730's landing; 210 since objectui#5232 added
307-
// `viewConfigPermissionDenied`; 211 since objectui#10636 added
308-
// `recordCountOne`, the record-count footer's singular; 217 since
309-
// objectui#11445 added six count-family slots (and retired `recordCountOne`,
310-
// which moved the split, not the total); 225 since objectui#11576 added the
311-
// create-view dialog's eight dataset-binding keys (and retired its four axis
312-
// keys); 227 since objectui#12418 added `resetView` and `resetViewTitle`,
313-
// the header's "Reset view" entry (and objectui#12504 retired
314-
// `viewConfigPermissionDenied`, which moved the split, not the total); 228
315-
// since objectui#13052 added `objectPackageDisabled`, the not-found view's
316-
// package-disabled sentence; 229 since objectui#13330 added
317-
// `managedUsers`, the managed environment's Users-list strings
318-
// (objectstack#25921 retired `ufTabs` and `viewTypeUnavailableShort`,
319-
// which moved the split, not the total). The RETIRED half is a ratchet that never
320-
// runs BACKWARDS — it is pinned twice above, and objectui#8754 advanced it
321-
// 116 -> 118 — while the surviving half is a live namespace that grows on a
322-
// new key and shrinks by exactly what the retired half gains. Splitting
323-
// them is the point: folding a new key into the total would be
324-
// indistinguishable from a retired key coming back.
325-
expect(RETIRED_OBJECT_VIEW_KEYS.length + SURVIVING_KEY_COUNT).toBe(229);
288+
it('the retired list names each key once, and there are packs to check it against', () => {
289+
// Guards the premise the rest of the file rests on, without a hand-typed
290+
// count: the names ARE the pin, so a second copy of the list's length only
291+
// turned every legitimate retirement into a two-place edit. What stays is
292+
// what a count never proved — no name is listed twice (a duplicate would
293+
// hide that the list is one key short of what its author meant), and the
294+
// checks below do not run over an empty list or an empty locale map.
295+
expect(RETIRED_OBJECT_VIEW_KEYS.length).toBeGreaterThan(0);
296+
expect(new Set(RETIRED_OBJECT_VIEW_KEYS).size).toBe(RETIRED_OBJECT_VIEW_KEYS.length);
297+
// The locale roster, not this namespace — pinned the same way across the
298+
// package's retirement files, and left to that roster's own sweep.
326299
expect(LANGS).toHaveLength(10);
300+
expect(LANGS).toContain('en');
327301
});
328302

329303
it('no pack defines any retired key, in any of the ten packs', () => {
@@ -345,8 +319,15 @@ describe('console.objectView config-panel keys are retired (objectui#4730)', ()
345319
// shape to pin is the surviving key SET, identical across packs — a root
346320
// that regrows a retired key is the regression this file exists for, and a
347321
// root that vanishes takes the live create-view dialog with it.
322+
//
323+
// The surviving set is DERIVED from `en`, never counted: `en` is the source
324+
// pack, so its keys are the namespace's live set by definition, and a key
325+
// added to every pack moves no assertion here. Whether `en` itself regrew a
326+
// retired key is the previous `it`'s question, not this one's.
348327
const enKeys = Object.keys(at(builtInLocales.en, 'console.objectView') as Record<string, unknown>);
349-
expect(enKeys).toHaveLength(SURVIVING_KEY_COUNT);
328+
// Anti-vacuity only: an empty `en` root would make every pack's "same set"
329+
// comparison below trivially true.
330+
expect(enKeys.length).toBeGreaterThan(0);
350331
// The one key a pack may hold that `en` lacks: an `en` count family's slot
351332
// for a CLDR category the pack's OWN language selects (`ru` `_few`, `ar`
352333
// `_two` — `all-locales-key-parity.test.ts` owns that rule repo-wide).
@@ -412,10 +393,10 @@ describe('console.objectView config-panel keys are retired (objectui#4730)', ()
412393
//
413394
// Call-SHAPED and key-SPECIFIC, the same trade `report-editor-retired-4145`
414395
// makes next door: a bare substring scan would score prose that NAMES a
415-
// retired key as the offence, and the 93 live keys in this same namespace
396+
// retired key as the offence, and the live keys in this same namespace
416397
// are exactly what must keep working. No dynamic-template check here, and
417398
// that is the one place this file diverges from its neighbour — a dynamic
418-
// read into this namespace can legitimately be reaching for one of the 93
399+
// read into this namespace can legitimately be reaching for one of the
419400
// survivors, so it is not evidence of a revival.
420401
const roots = ['packages/app-shell/src', 'packages/plugin-view/src'];
421402
const retired = new Set<string>(RETIRED_OBJECT_VIEW_KEYS);

0 commit comments

Comments
 (0)