Skip to content

Commit 96a7d20

Browse files
fix(example-showcase): the showcase_account extension declares the plural with its renamed label (#30738)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} --> _Requested by **Elon** · [project thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DBFhkQHTZPTwLSqiWCH7wN5)_ Fixes #30465 Clause-②: no Metadata-protocol: none Skills-docs: none — showcase example only ## 维护者速读 - 改了什么:showcase 的 `showcase_account` 对象扩展在改名 `Account (Success Overlay)` 的同时补上复数 `Accounts (Success Overlay)`。 - 为什么改:PR #30459 之后,只改 label 不写复数的扩展不再返回旧复数,列表标题会退回单数名。 - 风险与回滚:只动示例与一条 dogfood 用例,无发布包;回滚即撤本 PR。 - 先看:`examples/app-showcase/src/data/extensions/account.extension.ts`,再看 dogfood 文件里新增的 `[#30465]` 用例。 - 两分钟验证:`pnpm --filter @objectstack/dogfood exec vitest run test/showcase-object-extension-scalar-divergence.dogfood.test.ts`,预期 8 条全绿。 - 需要你做的:无,席位落地。 ## What changes - `examples/app-showcase/src/data/extensions/account.extension.ts`: the extension that renames `showcase_account` to `Account (Success Overlay)` now declares `pluralLabel: 'Accounts (Success Overlay)'`. Since PR #30459 a rename without a plural is served with no plural, and the Console titles a list by the served plural, else the label; so the list title had fallen back to the singular. - `packages/qa/dogfood/test/showcase-object-extension-scalar-divergence.dogfood.test.ts` (the file that already pins this object's served label): one new case. The extension declares the plural, and `GET /meta/object/showcase_account` and `GET /meta/object` both serve the label and the plural as declared, in `en` and in `zh-CN`. - **No translation change.** The card asked for a zh-CN plural in `examples/app-showcase/src/system/translations/**` only if one is wanted. That bundle is the showcase's code catalog, not a `.custom` pair. `translateObject` reads a scalar that diverges from the packaged base only from the pair's catalog (#8284 comparison rule, ADR-0146 D2). So a zh-CN entry there would not be served for either string. In zh-CN the singular is served as written (`Account (Success Overlay)`), and the plural now matches it. The pin measures exactly that. ## Acceptance notes - The commit message carries a `Fixes #30465` line. The dev contract keeps card relations to the PR body only. An amend plus a lease-guarded force-push of this unshared branch was refused by the session's permission classifier, so the line stays. It closes the same card this body closes. ## Verification <details><summary>Pin, reverse leg, typecheck, derived gates — head 32c6f57</summary> - Dogfood closure build: `pnpm --filter '@objectstack/dogfood^...' build` under `os-verify-lock.sh`: `VERDICT command-exit 0`. - Touched pin: `pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2 test/showcase-object-extension-scalar-divergence.dogfood.test.ts`: `VERDICT command-exit 0`, Test Files 1 passed, Tests 8 passed (8). - Reverse leg: the extension file restored to `origin/main` (`git restore --source=origin/main`; the showcase resolves from source, so no rebuild): the new case fails (exit 1, 1 failed), and the other 7 pass. Restored with `git checkout HEAD --`; blob equals HEAD and `git diff HEAD` is empty. - `pnpm --filter @objectstack/example-showcase --filter @objectstack/dogfood typecheck`: both ran `tsc --noEmit`, `VERDICT command-exit 0`. - eslint on the two changed files: exit 0. - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at 32c6f57: 55 commands, each run with its exit code captured before any pipe. 53 exit 0 on the first pass. `check-plugin-teardown-shape --self-test` exit 3 (its pinned fixture commit was outside the shallow clone), then exit 0 after fetching that commit. `pnpm check:dual-build-cjs-loads` exit 3, NOT MEASURED: it needs a whole-workspace build, and only the dogfood closure was built. CI runs it. - `dispatch-gates --ran`: 55 derived, 54 run, 1 NOT-MEASURED, 0 UNRUN, exit 0. - Changeset: none. `@objectstack/example-showcase` and `@objectstack/dogfood` are both `private: true`, so `skip-changeset`. - Full dogfood not run: the title has no `!` and `Clause-②` has no narrowing arm. </details> --- _Generated by [Claude Code](https://claude.ai/code/session_016WV4cytQjG9X1CrCgytUM5)_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7b2e557 commit 96a7d20

2 files changed

Lines changed: 30 additions & 0 deletions

File tree

‎examples/app-showcase/src/data/extensions/account.extension.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ import { defineObjectExtension } from '@objectstack/spec/data';
1313
export const AccountExtension = defineObjectExtension({
1414
extend: 'showcase_account',
1515
label: 'Account (Success Overlay)',
16+
// The plural travels with the label: an extension that renames an object
17+
// without one is served with no plural at all (the base's `Accounts` names
18+
// the old label), so the list title would fall back to the singular.
19+
pluralLabel: 'Accounts (Success Overlay)',
1620
fields: {
1721
loyalty_tier: {
1822
name: 'loyalty_tier',

‎packages/qa/dogfood/test/showcase-object-extension-scalar-divergence.dogfood.test.ts‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ import { writeBuildShapedArtifact } from './build-shaped-artifact.js';
118118

119119
/** What the showcase's `objectExtensions` entry declares on `main` today. */
120120
const EXTENSION_LABEL = 'Account (Success Overlay)';
121+
/** The plural the same extension declares with it (#30465). */
122+
const EXTENSION_PLURAL_LABEL = 'Accounts (Success Overlay)';
121123
/** What the showcase's `en` catalog declares for the same object. */
122124
const CATALOG_LABEL = 'Account';
123125

@@ -280,6 +282,30 @@ describe('dogfood: the object-extension fold and the i18n catalog disagree on sc
280282
expect(labelOf(singleBody?.item)).toBe(EXTENSION_LABEL);
281283
});
282284

285+
it('[#30465] the renamed object is served the extension\'s plural with its label, in en and zh-CN', async () => {
286+
// An extension that renames `label` with no `pluralLabel` is served with
287+
// no plural (the base's `Accounts` names the old label), and a list title
288+
// reads the plural first. So the extension declares the pair, and both
289+
// translated reads serve it. Both values diverge from the packaged base,
290+
// so the showcase's code catalog yields to them in every locale: zh-CN
291+
// serves the pair as written, the singular and the plural alike.
292+
const declared = (showcaseStack as any)?.objectExtensions
293+
?.find((e: any) => e?.extend === 'showcase_account');
294+
expect(declared?.pluralLabel).toBe(EXTENSION_PLURAL_LABEL);
295+
for (const locale of ['en', 'zh-CN']) {
296+
const headers = { Authorization: `Bearer ${token}`, 'Accept-Language': locale };
297+
const single: any = await (await stack.api('/meta/object/showcase_account', { headers })).json();
298+
const listRes = await stack.api('/meta/object', { headers });
299+
const listBody: any = await listRes.json();
300+
const listed = ((Array.isArray(listBody) ? listBody : (listBody?.items ?? listBody?.data ?? [])) as any[])
301+
.find((o) => o?.name === 'showcase_account');
302+
for (const [read, doc] of [['item', single?.item], ['list', listed]] as const) {
303+
expect(labelOf(doc), `${locale} ${read} label`).toBe(EXTENSION_LABEL);
304+
expect(doc?.pluralLabel, `${locale} ${read} pluralLabel`).toBe(EXTENSION_PLURAL_LABEL);
305+
}
306+
}
307+
});
308+
283309
it('[ADR-0131 D6] the Studio rename round-trip of a PACKAGED object is refused, hatch open — nothing is stored', async () => {
284310
// The round-trip the rename cases below used to perform: GET the served
285311
// document, rename it, PUT it back. Managed content is sealed, so the

0 commit comments

Comments
 (0)