Skip to content
This repository was archived by the owner on Oct 26, 2026. It is now read-only.

Commit b41ed7f

Browse files
fix(app-shell): the translation preview draws the positions and permissions groups, at an objectstack pin that declares them (objectui#13478) (#13485)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} --> _Requested by **Elon** · [project thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DSu9uoz6bLHskQmxnyBE86T)_ Fixes #13478 Clause-②: yes Metadata-protocol: none objectstack's `Console Pin Gate` is red on main because app-shell no longer type-checks against objectstack `47af3dde42` (objectstack-ai/objectstack#25701). That change added the `positions` and `permissions` groups to the per-app `TranslationDataSchema`, and `TranslationPreview`'s `CATEGORY_DEFS` is exhaustive over that schema's shape by `satisfies`. This PR adds the two rows and moves objectui's own pin to `47af3dde42`, so objectui type-checks against the same spec. Once it merges, objectstack can move its `.objectui-sha` past the merge commit (objectstack's own card names this one as its blocker). ## What changed - **`TranslationPreview.tsx`**: two `CATEGORY_DEFS` rows, in the schema's order (after `flows`, before `metadataForms`): `positions` with `Users` and `permissions` with `ShieldCheck`, the icons `PositionPreview` and `PermissionPreview` already draw. Both are nested groups (`flat: false`), so each card counts the group's top-level keys and samples them with their inner key count, as `objects` and `flows` do. - **`metadata-admin/i18n.ts`** (the designer table, the only pack these keys live in, `en` and `zh`): `engine.translationPreview.category.positions` = "Positions" / "岗位" and `engine.translationPreview.category.permissions` = "Permission Sets" / "权限集", the words the same table already uses for the two metadata types. - **Tests**: `TranslationPreview.categories-11765.test.tsx` gets a spec-valid fixture for each new group (its first case requires one per schema group). `adminPreviews.i18n-10862-s2.test.tsx` lists the two headings among the rendered locale sites and reads coverage over 13 groups (`2/13 (15%)`, was `2/11 (18%)`). - **Changeset** `.changeset/13478-translation-preview-positions-permissions.md`: `@object-ui/app-shell` patch. ## The pin - `.objectstack-sha` moves from `e649c2d91e` (the 18.1.0 release commit) to `47af3dde42bb4e77fd92025605b89b4a473295a4`. REST compare `e649c2d91e...47af3dde42`: `status: ahead`, `ahead_by: 51`, `behind_by: 0`; the bump script also reported "(on origin/main)". It is pinned at the card's floor, not at objectstack main's head: the four commits after it on objectstack main are left for the next bump. - Path: `scripts/bump-objectstack.sh 47af3dde42bb4e77fd92025605b89b4a473295a4 --no-commit` (built and packed the 7 packages), then `pnpm install`. - What moved besides the sha: three packed manifests in `.objectstack-pin/` (`core`, `lint`, `spec`), each only `zod` `^4.6.5` to `^4.6.6` (objectstack-ai/objectstack#25657). The lockfile already resolves `zod@4.6.7`, so **`pnpm-lock.yaml` is unchanged** and `pnpm install --frozen-lockfile` exits 0. - **Why the rows and the pin land together** (the seat's hypothesis; it held). At `e649c2d91e` the schema declares neither group: `grep -c '^ positions: z.record'` over that commit's `packages/spec/src/system/translation.zod.ts` reads 0, and 1 at `47af3dde42`; the same for `permissions`. Under `satisfies`, an extra key in an object literal is TS2353 "Object literal may only specify known properties" (probed with this repo's tsc 6.0.3 on a two-key `Record`). So the rows alone fail at the old pin, and the pin alone fails as the gate does. ## Clause-② reading (the claim asked for it) The claim declares `yes` because the seat could not tell whether `metadata-admin/i18n.ts` reaches app-shell's published entry, and the two lines above are copied from the claim. Measured on the built declaration closure (`packages/app-shell/dist`, walking the relative imports from `index.d.ts`, the only `exports` entry with types): - The closure holds 182 `.d.ts` files. `views/metadata-admin/i18n.d.ts` **is** one of them (the positive control), but its face is `t(key: string, …)`, `tFormat(key: string, …)` and the other helpers: the key table is not exported, and no key is a type. - Closure files that carry the string `translationPreview`: **0**. `previews/TranslationPreview.d.ts` is not in the closure at all. So the diff widens no published accept set and no public face of app-shell, and the reading is `no`. The declaration is the seat's, so the lines stay as claimed. ## Verification (local, this worktree, at the new pin) - `pnpm exec turbo run build --filter=@object-ui/console... --concurrency=2` (under the verify lock, 6144 MB heap): 35 of 35 tasks, including `@object-ui/app-shell`'s `tsc` and the console's vite build, SDUI manifest and plugin tsc. That is the build the Console Pin Gate runs, here at objectui's own pin. - `pnpm exec vitest run packages/app-shell/src/views/metadata-admin/ packages/types/` (under the lock): 980 files passed; 17091 tests passed, 1 skipped. - The three TranslationPreview test files: 63 of 63. Reverse check: with the two rows removed from `TranslationPreview.tsx` (on-disk count 0, back to 2 after the restore), the `categories-11765` file goes red on its order-and-coverage case and on every per-group case, the two new groups included. - Gates, each exit 0: `check:i18n-keys`, `check:i18n-designer-parity`, `check:i18n-dead-keys`, `check:i18n-drift`, `check:installed-pin-claims`, `check:spec-symbols`, `check:icon-record-names`, `check:control-bytes`, `check:new-line-citations` (0 new), `check-changeset-presence`, `check-changeset-no-major`, and `eslint` on the four changed source files. - `check:spec-floors`: exit 1 with the same two `floor-too-low` findings (`@object-ui/auth` `MEMBERSHIP_ROLE_EXTERNAL`, `@object-ui/types` `navItemVisibleDataNeedsDataSource`) that #13472 recorded as pre-existing on main. This diff touches neither package. The gate runs on push and schedule with `--cross-check`, which is its measurement, and that run is green on main `cd70b78`. NOT MEASURED locally (left to CI): the rest of the workspace `vitest` shards, `lint` and `type-check` across all packages, and e2e. ## Acceptance notes - objectui#13477 (the label render of the same objectstack change) is a different defect; it remains open and nothing here touches it. - [x] Adds no product-specific widget, dialog, hook or string (see AGENTS.md › Product-neutral renderer) Implemented by the ui lane card thread, session `https://claude.ai/code/session_01Usjyy1hT9FNxZHea9u7drp`; claim on the card, comment 6391374949. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Usjyy1hT9FNxZHea9u7drp --- _Generated by [Claude Code](https://claude.ai/code/session_01Usjyy1hT9FNxZHea9u7drp)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 723776b commit b41ed7f

10 files changed

Lines changed: 37 additions & 5 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"@object-ui/app-shell": patch
3+
"@object-ui/i18n": patch
4+
---
5+
6+
Clause-②: yes
7+
Metadata-protocol: none
8+
9+
The metadata designer's translation preview draws the `positions` and `permissions` groups that `@objectstack/spec` now declares on a translation bundle, each with its own card, count and sample, and counts them in the coverage bar. Before, the preview did not type-check against that spec, and a bundle that translated only position or permission-set labels read as empty.
10+
11+
`isSpecTranslationData` recognises a served translation document by those two groups as well, so a bundle that translates only positions or permission sets is namespaced under `app` like any other spec bundle instead of being returned untransformed.

‎.objectstack-pin/core/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"dependencies": {
3030
"@objectstack/spec": "18.1.0",
3131
"@objectstack/types": "18.1.0",
32-
"zod": "^4.6.5"
32+
"zod": "^4.6.6"
3333
},
3434
"keywords": [
3535
"objectstack",

‎.objectstack-pin/lint/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"devDependencies": {
5151
"@types/node": "^26.6.3",
5252
"vitest": "^4.1.11",
53-
"zod": "^4.6.5"
53+
"zod": "^4.6.6"
5454
},
5555
"keywords": [
5656
"objectstack",

‎.objectstack-pin/spec/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@
282282
},
283283
"dependencies": {
284284
"pg-connection-string": "^2.14.0",
285-
"zod": "^4.6.5"
285+
"zod": "^4.6.6"
286286
},
287287
"peerDependencies": {
288288
"ai": "^7.0.0"

‎.objectstack-sha‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
e649c2d91e270e3da26820bcfe54a28445305a75
1+
47af3dde42bb4e77fd92025605b89b4a473295a4

‎packages/app-shell/src/views/metadata-admin/i18n.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,6 +1785,8 @@ const ENGINE_STRINGS_EN: Record<string, string> = {
17851785
'engine.translationPreview.category.datasets': 'Datasets',
17861786
'engine.translationPreview.category.pages': 'Pages',
17871787
'engine.translationPreview.category.flows': 'Flows',
1788+
'engine.translationPreview.category.positions': 'Positions',
1789+
'engine.translationPreview.category.permissions': 'Permission Sets',
17881790
'engine.translationPreview.category.metadataForms': 'Metadata Forms',
17891791
'engine.translationPreview.category.settingsCommon': 'Settings UI',
17901792
'engine.translationPreview.coverage': 'Category coverage:',
@@ -5407,6 +5409,8 @@ const ENGINE_STRINGS_ZH: Record<string, string> = {
54075409
'engine.translationPreview.category.datasets': '数据集',
54085410
'engine.translationPreview.category.pages': '页面',
54095411
'engine.translationPreview.category.flows': '流程',
5412+
'engine.translationPreview.category.positions': '岗位',
5413+
'engine.translationPreview.category.permissions': '权限集',
54105414
'engine.translationPreview.category.metadataForms': '元数据表单',
54115415
'engine.translationPreview.category.settingsCommon': '设置界面',
54125416
'engine.translationPreview.coverage': '分类覆盖率:',

‎packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.categories-11765.test.tsx‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ const ONE_ENTRY: Record<string, { data: Record<string, unknown>; key: string; ro
5858
},
5959
pages: { data: { home: { label: 'Accueil' } }, key: 'home', row: () => keys(1) },
6060
flows: { data: { onboarding: { label: 'Intégration' } }, key: 'onboarding', row: () => keys(1) },
61+
positions: { data: { legal_counsel: { label: 'Conseiller juridique' } }, key: 'legal_counsel', row: () => keys(1) },
62+
permissions: {
63+
data: { contract_reviewer: { label: 'Revue des contrats', description: 'Lire et approuver les contrats' } },
64+
key: 'contract_reviewer',
65+
row: () => keys(2),
66+
},
6167
metadataForms: { data: { object: { label: 'Objet' } }, key: 'object', row: () => keys(1) },
6268
// `settingsCommon` is one strict object, not a record of author-named nodes:
6369
// its members (`sourceLabels`) are what the card counts and samples.

‎packages/app-shell/src/views/metadata-admin/previews/TranslationPreview.tsx‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ import {
4343
MessageCircle,
4444
PanelsTopLeft,
4545
Settings2,
46+
ShieldCheck,
4647
SquareChevronDown,
48+
Users,
4749
Workflow,
4850
} from 'lucide-react';
4951
import { EmptyDescription } from '@object-ui/components';
@@ -94,6 +96,8 @@ const CATEGORY_DEFS = {
9496
datasets: { label: 'engine.translationPreview.category.datasets', icon: Database, flat: false },
9597
pages: { label: 'engine.translationPreview.category.pages', icon: PanelsTopLeft, flat: false },
9698
flows: { label: 'engine.translationPreview.category.flows', icon: Workflow, flat: false },
99+
positions: { label: 'engine.translationPreview.category.positions', icon: Users, flat: false },
100+
permissions: { label: 'engine.translationPreview.category.permissions', icon: ShieldCheck, flat: false },
97101
metadataForms: { label: 'engine.translationPreview.category.metadataForms', icon: FileText, flat: false },
98102
settingsCommon: { label: 'engine.translationPreview.category.settingsCommon', icon: Settings2, flat: false },
99103
} satisfies Record<TranslationGroup, Omit<CategoryDef, 'key'>>;

‎packages/app-shell/src/views/metadata-admin/previews/adminPreviews.i18n-10862-s2.test.tsx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,14 +461,16 @@ describe('TranslationPreview reads the designer locale (objectui#10862)', () =>
461461
{ key: 'engine.translationPreview.category.datasets' },
462462
{ key: 'engine.translationPreview.category.pages' },
463463
{ key: 'engine.translationPreview.category.flows' },
464+
{ key: 'engine.translationPreview.category.positions' },
465+
{ key: 'engine.translationPreview.category.permissions' },
464466
{ key: 'engine.translationPreview.category.metadataForms' },
465467
{ key: 'engine.translationPreview.category.settingsCommon' },
466468
{ key: 'engine.translationPreview.categoryEmpty' },
467469
{ key: 'engine.translationPreview.more', vars: { count: 1 } },
468470
{ key: 'engine.translationPreview.keyCountOne', vars: { count: 1 } },
469471
]);
470472
// The bundle's own locale is author data, not the designer's language.
471-
expectAsWritten(['French', 'fr-FR', 'account', '"Un"', '2/11 (18%)']);
473+
expectAsWritten(['French', 'fr-FR', 'account', '"Un"', '2/13 (15%)']);
472474
});
473475
}
474476
});

‎packages/i18n/src/utils/spec-translations.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ const SPEC_TRANSLATION_GROUPS = [
8989
// `picklist` metadata kind); added at that bump (objectui#11438). The
9090
// transform below copies it through onto `app` like any other group.
9191
'picklists',
92+
// objectstack `47af3dde42` serves `positions` and `permissions` (positions
93+
// and permission sets are translatable); added at the pin move to that
94+
// commit (objectui#13478), copied through onto `app` the same way.
95+
'positions',
96+
'permissions',
9297
] as const;
9398

9499
function isPlainObject(value: unknown): value is Record<string, unknown> {

0 commit comments

Comments
 (0)