Skip to content

fix(app-shell): the hook and action inspectors, and the shared inspector defaults, read their words in the designer locale (objectui#10586) - #10678

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-10586-inspector-strings-i18n-pr2
Sep 25, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-10586-inspector-strings-i18n-pr2

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #10586
PR 2 of 2. PR objectui#10651 (PR 1: the dataset, reference and condition surfaces) has merged, and this PR completes the card.

Clause-②: no

The English literals of HookDefaultInspector and ActionDefaultInspector move behind the designer catalogue's t(), each with an en and a zh row. The English prop defaults of InspectorComboField and _shared.tsx resolve in the active locale when a caller passes none. en renders byte-identical. No schema, export or accept set moves.

What changes

file locale channel catalogue rows (en + zh) existing rows reused
inspectors/HookDefaultInspector.tsx the host's locale prop, through its existing tr engine.inspector.hook.* (27) none
inspectors/ActionDefaultInspector.tsx the host's locale prop: tr in the body, and a locale prop into ObjectPicker and ParamOptionsEditor engine.inspector.action.* (125) engine.inspector.reorder.up / .down (the per-input move buttons)
inspectors/InspectorComboField.tsx useMetadataLocale(), as InspectorSelectField's default flag already does engine.inspector.combo.* (4): search, noMatch, loading, useCustom engine.form.selectEllipsis (the placeholder default)
inspectors/_shared.tsx useMetadataLocale() none engine.close (InspectorShell), engine.inspector.reorder.up / .down (InspectorReorderButtons), engine.form.optionsLoadFailedTitle (InspectorSelectField's roster-failure notice)
metadata-admin/i18n.ts the 156 rows above, in ENGINE_STRINGS_EN and ENGINE_STRINGS_ZH; pnpm check:i18n-drift reads "0 en value(s) changed (156 key(s) added, 0 removed)"
  • Option tables are keyed by the stored value (KeyedOption plus localizeOptions, the PR 1 shape) and resolved at render, so stored values do not move. PARAM_TYPE_OPTS keeps its satisfies check against ResolvableParamFieldType, and the objectui#6538 pin reads only its .value. LOCATION_LABELS stays a total Record over ActionLocation, whose values are now keys.
  • Sentences with a node in them (the pinned-to-script note, the sandbox signature, both capability-gate sentences, the combo's custom row) use {token} slots, filled with a file-local split-and-interleave (the ResourceEditPage shape).
  • A caller's own closeLabel, upLabel / downLabel, rosterFailureLabel, placeholder, searchPlaceholder or emptyText still wins. Each default is prop ?? t(KEY, locale). InspectorReorderButtons reads its locale before its early return, so the hook order does not depend on props.
  • Left literal on purpose: lifecycle event names, HTTP methods, the code samples in the body placeholders, the sample values (status, done, 100, object.viewKey, the API path), the script type name, the move arrows and the #1 counters.
  • zh wording follows the catalogue's own rows: pageBlock's variant and location labels, the engine.fieldType.* names, and designer.field.* (显示名 / 占位符 / 必填).

Premise check: the dispatch's mechanism hypotheses, measured

  • H1 (floor census at 785d3a7). The t() / tr() counts reproduce: 8 in HookDefaultInspector and 5 in ActionDefaultInspector. The literal counts depend on how the regex is spelled. A loose spelling reads 22 and 66, and a strict one (a capital then a lower-case letter) reads 18 and 54. Neither reproduces the 15 and 47 in the dispatch. Reading both files end to end gives the full set, which is what the pin lists: 27 hook strings and 121 action strings over the fixtures. They include option labels, aria-label / placeholder values and composed sentences the regex cannot see.
  • H2 (the shared defaults). Both atoms read useMetadataLocale(). _shared.tsx already read it for the objectui#9652 flag, and InspectorComboField now imports it. Reach under zh today, over every in-tree call site, read with a tag scanner that skips comments and cross-checked against a plain grep count:
default call sites reach it
InspectorComboField emptyText 18 18
InspectorComboField Use custom row 18 17 (one passes allowCustom={false})
InspectorComboField loading trigger 18 15 pass loading
InspectorComboField searchPlaceholder 18 4 (AppNavInspector: the filter-row field picker, the object, view and typed-target pickers)
InspectorComboField placeholder (Select…) 18 3 (AppNavInspector: the object, view and typed-target pickers)
InspectorReorderButtons up / down 5 1 (ObjectFieldInspector's header)
InspectorShell close label 21 0: 19 pass closeLabel, and the other 2 hide the button
InspectorSelectField roster-failure notice 4 (every roster= site) 0: all 4 pass rosterFailureLabel

InspectorShell, InspectorSelectField and InspectorComboField are not exported from the package's exports, so these are the only callers. The placeholder default is outside the claim's enumerated list. It is included under the bounded-fix rule: the same defect class, in the same function signature of a file on the claim, mechanical, and held by the same gates. It is named here as a deviation.

  • H3 (en byte-identical). Measured as below: an en DOM dump, base against fix.
  • H4 (key collisions). Both tables have 0 duplicate keys after the merge with PR 1 (EN 1950 keys, ZH 2005 keys, 159 hook / action / combo keys in each, the 3 older ones included). pnpm check:i18n-designer-parity exits 0.
  • H5. PR objectui#10651 had merged. This branch merged origin/main 02e6d36 without conflicts. The two PRs' i18n.ts rows sit in separate hunks.

en byte-identical: one-off measurement, not a committed test

A throwaway harness was built from the pin by disabling its describe blocks and adding one dump case. It rendered every pin fixture under en-US and wrote document.body.innerHTML after each state. That covers the 2 hook fixtures, the 9 action fixtures, the combo host in flight and answered (plus its two intermediate popover states), the reorder host, the two primitives, and a caller-label case: 19 states in all. It ran once on the fix (0a024c1) and once with the five source files checked out from 785d3a7. Both dumps were 312084 bytes with sha256 0049a6056c57d289d9a42199cedf93fe9589fd2793c4fbf706471d044e3c30c9, and cmp exited 0. The dump contains the branches it claims to cover (for example the capability-gate sentence twice, the pinned-to-script note twice, and the custom row once). The harness was deleted and never committed. Restore was proven with git diff HEAD at 0 lines and each path's git hash-object equal to its HEAD blob.

Pin: red on base, green on head

inspectors/inspectorStrings.hookAction.i18n-10586.test.tsx has 11 cases:

  • for each of the two inspectors, the combo (through AppNavInspector) and the other shared defaults (the reorder pair through ObjectFieldInspector; the close label and the roster notice read off their primitives, which no host reaches): a zh sweep with exact membership over element text, text nodes, aria-label, title and placeholder, then named zh samples read back from the catalogue behind a not-the-key / not-the-en-row guard; and an en case in which every pre-fix string still renders, word for word;
  • a caller-label case under zh;
  • two lit controls on text that was already translated: ObjectDefaultInspector (the locale prop channel) and InspectorSelectField's default flag (the useMetadataLocale() channel).

Measurements:

  • Base leg: the five source files from 785d3a7, and the pin from 0a024c1. Result Tests 4 failed | 7 passed (11), exit 1. The four zh sweeps list the English still rendered: hook 27 of 27, action 121 of 121, combo 5 of 5, shared 4 of 4. The 7 passes are the four en cases, the caller-label case and the two lit controls.
  • Keys-only leg: i18n.ts from head and the four components from 785d3a7. Result Tests 4 failed | 7 passed (11), exit 1, with the same four lists. The catalogue rows alone change nothing on screen.
  • Head: Tests 11 passed (11). At 7c35ba6, this pin and PR 1's together read Tests 21 passed (21), exit 0.
  • Restore after each leg: git checkout HEAD -- on the mutated paths, then git diff HEAD 0 lines and each git hash-object equal to its HEAD blob.

Gates

All at 7c35ba6 (after the merge of 02e6d36), heavy runs through os-verify-lock:

gate exit reading
pnpm exec vitest run packages/app-shell/src/views/metadata-admin/, as 4 path-split runs (inspectors/; previews/ + external/ + __tests__/; the top-level files in two halves) 0, 0, 0, 0 102 + 64 + 72 + 72 = 310 files, which equals the directory's tracked test population; 1411 passed (1 skipped) + 754 + 500 + 876 tests
readers outside that directory: 9 studio-design tests, 2 data-objectstack, 3 packages/i18n, and the check-i18n-call-site-keys, check-i18n-dead-keys and vite-dts-explicit-extensions script tests 0 17 files, 400 tests
pnpm turbo run type-check --filter @object-ui/app-shell --concurrency=2 0 30/30 tasks, and app-shell:type-check was a cache miss, so both tsc projects ran. The previous run at fca6999 was red (TS2322 in the new pin's two primitive fixtures), which shows the pin is in a compiled project; 7c35ba6 fixes it
eslint, plain form, on the 6 changed TS files 0 --format json: 6 files, 0 errors, 7 warnings. The per-file counts equal base: 0 each, except the 7 react-refresh/only-export-components warnings that _shared.tsx already had. The new pin has 0
check:i18n-designer-parity, check:i18n-keys, check:i18n-drift, check:i18n-dead-keys (0 of the new keys reported), check:control-bytes, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:new-line-citations (0 new), check:changeset-claims, check:pending-changeset-literals, check-changeset-presence (6 source files of 1 released package, 1 changeset), check-changeset-no-major 0 each
derived from this diff: check:spec-symbols, check:action-forward-parity, check:phantom-deps 0 each
  • The eslint narrowing is a measurement, not a skip. The population is the changed .ts / .tsx files, which the config's **/*.{ts,tsx} block covers, and the JSON output counts 6 files. eslint.config.js configures no type-aware linting (a grep for projectService or a parserOptions project exits 1), so this diff cannot move an untouched file's verdict.
  • check:changeset-claims reports 4 pending bodies that name a file this PR edits: 6310-designer-formula-key-retired, 6329-actionparam-one-authority, 7125-dashboard-empty-state-keys-retired and 8632-malformed-picklist-option-loud. I read each paragraph, and all four still hold. The 6310 and 7125 paragraphs are about keys this PR does not touch. In 6329, "the eight offered spellings … are now checked against the vocabulary at compile time" still holds: PARAM_TYPE_OPTS keeps its satisfies check. 8632 restates the carve-out this PR follows.
  • NOT MEASURED: check:eager-locale-catalogues and the eager-closure budget, which read a built apps/console bundle (performance-budget.yml runs both); the repo-wide pnpm lint and the full pnpm test shards belong to CI.
  • origin/main moved to d08ab2f after the merge. Its three commits touch components, core, fields, plugin-charts and types only, and no file of this PR. So the branch was not merged again; the merge queue rebuilds on current main.

Round 2 (b5f081d): merged origin/main 7baede3 (conflict-free; no file of this PR moved; the incoming commits carry the objectui#10645 test change, PR objectui#10677). At b5f081d, through os-verify-lock: both card pins Tests 21 passed (21), exit 0; check:i18n-designer-parity exit 0; pnpm turbo run type-check --filter @object-ui/app-shell 30/30 tasks, exit 0 (app-shell cache miss, so it executed).

Acceptance notes

  • Both inspectors' localize() helper reads an authored { en, … } label map as its en entry first. That is author data being edited, not chrome, and it is outside this card.
  • DatasetDefaultInspector (PR 1) and ActionDefaultInspector (this PR) each carry a file-local copy of the same slot helper, withSlots. A shared export from _shared.tsx would add an eighth react-refresh/only-export-components warning there, so neither PR moved it.
  • The flow-reference picker, the icon picker and SchemaForm render inside these inspectors with their own strings. They are outside this card.
  • The zh rows for the combo's custom row and the capability-gate self clause keep the en layout's separating space before the slotted node.

Session: https://claude.ai/code/session_01KUxVUa7e39aNjhkKi1gsoy


Generated by Claude Code

…shell defaults, read their words in the designer locale (objectui#10586)

HookDefaultInspector and ActionDefaultInspector route their section
titles, field labels, placeholders, hints, option labels and accessible
names through the designer catalogue under engine.inspector.hook.* and
engine.inspector.action.*, with an en and a zh row each.
InspectorComboField's placeholder, search, empty, loading and custom-row
words, and _shared.tsx's close, reorder and roster-failure defaults,
resolve through useMetadataLocale() when the caller passes none. en
renders the same text as before.

Claude-Session: https://claude.ai/code/session_01KUxVUa7e39aNjhkKi1gsoy
Co-authored-by: Claude <noreply@anthropic.com>
…signer locale (objectui#10586)

A zh sweep per surface asserts that none of the pre-fix English renders,
an en read asserts every pre-fix string still does, named zh samples are
read back from the catalogue, a caller's own label still wins, and two
lit controls read translations that were already there.

Claude-Session: https://claude.ai/code/session_01KUxVUa7e39aNjhkKi1gsoy
Co-authored-by: Claude <noreply@anthropic.com>
…default strings patch

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

`Fixture` is `(lang) => Promise<void> | void`, and the two arrows that read
the close label and the roster-failure notice off their primitives
returned `inLang`'s `RenderResult`. `tsc -p tsconfig.test.json` refused
them (TS2322); block bodies fix it.

Claude-Session: https://claude.ai/code/session_01KUxVUa7e39aNjhkKi1gsoy
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 4 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6310-designer-formula-key-retired.md

  • names packages/app-shell/src/views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Not touched: designer.field.formula ('Formula (CEL)') in packages/app-shell/src/views/metadata-admin/i18n.ts, a different and live key belonging to metadata-admin's ObjectFieldInspector — the surface that still authors formula expressions.

.changeset/6329-actionparam-one-authority.md

  • names ActionDefaultInspector.tsx → packages/app-shell/src/views/metadata-admin/inspectors/ActionDefaultInspector.tsx — edited by this change

    ActionParam has one authority again (objectui#6329). The name was declared three times, not twice as the card counted: @object-ui/types publishes it — derived from the spec's ActionParamSchema input, with its own parity suite — and ActionDefaultInspector.tsx and ActionPreview.tsx each carried a module-local interface of the same name. app-shell already read the published one elsewhere (utils/resolveActionParams.test.ts), so both locals were shadows. They are deleted, not reconciled against each other, under the 2026-08-25 family ruling 甲A1.

.changeset/7125-dashboard-empty-state-keys-retired.md

  • names packages/app-shell/src/views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Not touched: table.noRows ('No rows to display') and engine.form.noRows (packages/app-shell/src/views/metadata-admin/i18n.ts, read at widgets.tsx) — two different, same-named keys in different namespaces. Nor the comments in WidgetEmptyState.tsx, DatasetWidget.tsx, ObjectDataTable.tsx and PivotTable.tsx that record WHY three widgets with three strings became one shared empty state; the packs' own comment keeps that rationale and now names the retirement instead of a row that is gone.

.changeset/8632-malformed-picklist-option-loud.md

  • names views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Two new strings land in the designer's own en / zh tables — the metadata-admin console owns its strings in views/metadata-admin/i18n.ts and is deliberately outside the ten locale packs (packages/i18n/README.md, "Scope — the engine.* carve-out").

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Compared the checked-out tree with 7baede374 (merge-base with origin/main): 6 file(s) changed outside .changeset/, read against 1516 pending declaration(s) that publish a body (2102 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3060.1 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-CDNJY6-1.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.57KB 6.15KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.17KB 10.58KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 547.22KB 130.96KB
core (index.js) 9.52KB 3.79KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 259.19KB 65.61KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.49KB 14.64KB
plugin-charts (index.js) 76.80KB 21.35KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.60KB 35.41KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.85KB 61.58KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 152.55KB 39.16KB
plugin-gantt (index.js) 169.71KB 41.93KB
plugin-grid (index.js) 216.65KB 59.28KB
plugin-kanban (index.js) 48.35KB 15.08KB
plugin-list (index.js) 115.77KB 28.75KB
plugin-map (index.js) 23.01KB 7.60KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.75KB 8.98KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.77KB 21.99KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 116.21KB 38.14KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

CI red at 7c35ba6 on the objectui#10645 test only, so this PR waits for that fix to land

domain:ui seat 5, session_01KUxVUa7e39aNjhkKi1gsoy, 2026-09-25T16:56Z. The at-tier contract review of this head PASSed; its record follows. One required check is red:

  • The check: Test (shard 2/8), with a single failing test out of 6952 in the shard: apps/console/src/pages/developer/ApiConsolePage.requestPreset-10591.test.tsx:299, the console still on its boot splash. That is objectui#10645.
  • Why it is not this PR's: this PR touches only packages/app-shell/src/views/metadata-admin/.
  • The fix: PR objectui#10677 carries it, and it is in the merge queue now, ACCEPTed.
  • What happens next: once that fix is on main, this branch takes one origin/main merge, CI re-runs, and the seat re-reviews the merged head and ACCEPTs it. This PR does not widen.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 7c35ba6f8b30997b90c73f03bdc902e6a51b4ed2

Read for this record: card objectui#10586 (body and all 9 comments), the triage 5832133290, the three claims 5832991567 / 5834151314 / 5834971726, the packages/i18n README carve-out section (objectui#4662) the claims cite, PR objectui#10678 (body, 7-file list, check-runs at the head, three reads), the diff 02e6d3668341b231bc0009b5fa74d33d83955e33..7c35ba6f8b30997b90c73f03bdc902e6a51b4ed2 via refs/review/pr-10678 and refs/review/main, AGENTS.md 版本号策略 and the changeset-presence rule, .changeset/config.json. The os-dev-report was not used as evidence. Zero GitHub writes. Line numbers below are at the head.

① Derived judgments

Binding text. Triage 5832133290: 「route the strings through engine.inspector.* / engine.form.* with keys in every built-in locale; en stays byte-identical」. Claim 5834151314 reads 「keys in every built-in locale」 as the designer catalogue's two built-in locales (the objectui#4662 carve-out; the README records that no engine.* key exists in any of the ten packs). Claim 5834971726 file surface: inspectors/HookDefaultInspector.tsx, inspectors/ActionDefaultInspector.tsx, inspectors/InspectorComboField.tsx (its searchPlaceholder / emptyText defaults, the Loading… trigger and the Use "…" custom row), inspectors/_shared.tsx (its Close / Move up / Move down / Options could not be loaded defaults), their rows in i18n.ts (ENGINE_STRINGS_EN and ENGINE_STRINGS_ZH), pins beside them, one .changeset/10586-…-2.md; ⛔ not packages/i18n/src/locales/.

  1. Surface. 7 files: the four claimed components, metadata-admin/i18n.ts (+325/-0), one new pin inspectors/inspectorStrings.hookAction.i18n-10586.test.tsx (+615), one changeset .changeset/10586-inspector-strings-i18n-2.md (+23). Nothing under packages/i18n/. Right against the claim.

  2. Catalogue rows, i18n.ts. 156 new keys in each table: engine.inspector.hook.* 27 (EN 795–823, ZH 3039–3066), engine.inspector.action.* 125 (EN 824–952, ZH 3067–3194), engine.inspector.combo.* 4 (EN 1220–1225, ZH 3513–3517). Scanned both tables at the head on top of PR 1's rows: EN 1950 keys / 1950 unique, ZH 2005 / 2005, 0 duplicate keys; the new EN and ZH key sets are equal; no new zh row equals its en row; every {slot} name set matches between en and zh. The pre-existing hook.kind, action.kind, action.close rows are not re-declared. Right against the triage as the carve-out ruling reads it.

  3. en byte-identical, row by row. Every removed literal in the four components was compared with the row that replaced it at code-point level: 133 rows match a removed literal exactly by script; the remaining 25 were checked by hand because the base spelled them as multi-line JSX text or with a node inside. Multi-line JSX (action.updateHint 150-row, action.noPlacement): JSX collapses the line breaks to single spaces, and the row carries exactly that collapsed text. Slotted rows: action.pinnedScript (head 680) splits into "Pinned to " + the code element + the rest, the base's three children; action.scriptBodyHint (772) likewise around the signature code element; action.capabilityGate (879) with {capabilities} as the mono span and {whom} as capabilityGateAll "all of them" / capabilityGateOne "it", then capabilityGateSelf (887) after the same {' '}; the combo's Use {value} splits to "Use " and an empty tail (188, 233), the curly quotes staying in JSX as before. action.removeOption "Remove option {index}" through tFormat (454) yields "Remove option 1" for the base's template literal. Code points preserved: U+2014 em dashes, U+2019 apostrophes, U+2026 ellipses, U+00B7 middle dots in the location labels, U+2265 in the two AI-description rows. Reused rows equal the removed defaults byte for byte: engine.form.selectEllipsis "Select…", engine.close "Close", engine.inspector.reorder.up / .down "Move up" / "Move down", engine.form.optionsLoadFailedTitle "Options could not be loaded", and the new combo.loading "Loading…". Serialized markup is identical; the only difference is invisible text-node boundaries where the base used {' '} splits (the capability sentence, the three "Add …" buttons). Right.

  4. ActionDefaultInspector.tsx. KeyedOption + localizeOptions (69–75) resolve option labels at render; the six option tables keep every stored value (87–129, 289–292). PARAM_TYPE_OPTS (147–156) keeps its satisfies check over ResolvableParamFieldType; LOCATION_LABELS (202–209) stays total over ActionLocation with keys as values; TARGET_FIELD (221–227) keys the worded placeholders and keeps the sample values object.viewKey and /api/v1/… literal; targetCfg (637–642) still hands ActionTargetField (239) plain strings, so the * suffix composition is untouched. withSlots (81–85) is the ResourceEditPage split-and-interleave. ObjectPicker (350) and ParamOptionsEditor (415–420) gain a required locale and are passed it (664, 830); both are file-local. The per-input move buttons (807–808) reuse engine.inspector.reorder.up / .down, en identical to the removed aria-labels. 121 strings, all in the pin's ACTION_EN. Left literal: HTTP methods, status / done / 100, the JS sample placeholder, the # counters and arrows. Right.

  5. HookDefaultInspector.tsx. BODY_LANG_OPTS carries labelKey (49–52), resolved through tr at 304; the pre-existing tr (125) is the host locale prop. 27 sites between 215 and 390, including the answered-empty roster sentence (236) and the two body hints (322–323). The priority 100 and the expression / JS body samples (317) stay literal: not words. Right.

  6. InspectorComboField.tsx. Imports t, useMetadataLocale (50); the three word props are renamed to *Prop (136–138); useMetadataLocale() runs unconditionally after the two useStates (147) and each default is prop ?? t(key, locale) (148–150); the loading trigger (167) and the custom row (188, 233) read the catalogue. ?? has the same undefined-only semantics as the former default parameters, and the props are typed as optional strings, so a caller's own placeholder / searchPlaceholder / emptyText still renders exactly. useMetadataLocale() is useObjectTranslation().language mapped to zh-CN / en-US (i18n.ts 5378–5381), the channel InspectorSelectField's flag already reads (_shared.tsx 415): the active designer locale, not a hard-coded one. The placeholder default ("Select…") is outside the claim's enumerated list: same defect class, same signature, in a claimed file; the en text is identical (U+2026 on both sides) and a caller's placeholder wins (148). Accepted as the named deviation.

  7. _shared.tsx. InspectorShell (60–62): closeLabelProp ?? t('engine.close', locale). InspectorReorderButtons (106–118): the hook is read at 115 before the early return at 116, so hook order does not depend on props; labels at 117–118. InspectorSelectField (326, 355, 415–416): rosterFailureLabelProp ?? t('engine.form.optionsLoadFailedTitle', locale). In-tree callers, verified by grep: 21 InspectorShell sites, 19 pass closeLabel and 2 hideClose; 5 InspectorReorderButtons sites, only ObjectFieldInspector 797–802 passes no labels; all 4 roster= sites pass rosterFailureLabel. A caller's own text is untouched. Right.

  8. Missing asks. None: every item of claim 5834971726's surface is present, and no user-visible English literal remains in the four components (attribute, JSX-text and ternary sweeps at the head).

  9. Not asked. The placeholder default (item 6, accepted); the two file-local helpers and the JSDoc header edits in InspectorComboField.tsx and _shared.tsx, which document the change in the claimed files. Nothing else.

  10. Existing pins. None edited or deleted: the file list adds one test and modifies no test. The objectui#6538 pin ActionDesigner.paramVocabulary.test.tsx:80 maps PARAM_TYPE_OPTS to its .value field only, so the label to labelKey rename does not weaken it.

  11. New pin can fail. inspectorStrings.hookAction.i18n-10586.test.tsx, 11 cases. Negative leg: the zh sweeps expectNone (172) over 27 / 121 / 5 / 4 literals (recounted: all unique) fail on the base by construction, since SectionHeader title="Basics" and its siblings rendered unconditionally; the named zhRow samples (160–165) refuse an echoed key or an en-equal row, so a fixture that rendered nothing cannot pass vacuously. Reach: the en legs expectAll (167) require every listed literal to render. Caller-label case (584). Lit controls (603–614): ObjectDefaultInspector's designer.object.section.basic on the host locale channel and InspectorSelectField's engine.form.notFound flag on the useMetadataLocale() channel; both keys exist in both tables. Hosts verified: AppNavInspector 401–409 passes no placeholder / searchPlaceholder / emptyText; ObjectFieldInspector 797–802 passes no reorder labels. Not run here (read-only review); judged from the code and from CI (the pin sits in shard runs that passed, see ③).

② Semver level

'@object-ui/app-shell': patch. AGENTS.md 版本号策略: no major in the fixed group, minor / patch evolve independently; a change to a published package's src/ owes a changeset (AGENTS.md line 168). A behaviour fix with no export, type or accept-set change is patch. Right. Not docs-only, so the empty-frontmatter rule does not apply.

Changeset prose, sentence by sentence: title "the hook and action inspectors, and the shared inspector defaults, read their words in the designer's locale" — true. "rendered their section titles, field labels, placeholders, hints, option labels and accessible names as English literals" — true (the removed lines). "InspectorComboField and _shared.tsx fell back to English defaults wherever a caller passed none: the combo's placeholder, search box, empty state, loading text and "Use" row, and the shell's close label, the reorder pair's names and the roster-failure notice" — true (the removed defaults). "So a zh-CN author read English inside an otherwise Chinese designer" — true. "The two inspectors now read the designer's own string catalogue under engine.inspector.hook.* and engine.inspector.action.*, each row in en and zh, from the locale their host already passes" — true (tr at Hook 125 / Action 506; locale into the two helpers). "The combo's words are new engine.inspector.combo.* rows" — true for search, noMatch, loading, useCustom. "The other defaults reuse existing rows (engine.form.selectEllipsis, engine.close, engine.inspector.reorder.*, engine.form.optionsLoadFailedTitle), and they follow the active language through useMetadataLocale()" — true. "A label the caller passes still wins" — true. "The en-US text is unchanged" — true (item 3). "Stored values, lifecycle event names, HTTP methods, code samples and sample values are not words and stay as they were" — true.

No content/docs/** or README prose is added. The added JSDoc (InspectorComboField.tsx 88–95; _shared.tsx 19–28, 43, 94, 320–325, 393) states: the atoms read useMetadataLocale() as InspectorSelectField's flag does — true; a caller's own text is kept — true; the close label defaults to engine.close, the reorder pair to engine.inspector.reorder.up / .down, the roster notice to engine.form.optionsLoadFailedTitle — true.

③ Boundary flags

  • PR body flags. (1) The placeholder default deviation: judged in ① item 6, accepted. (2) origin/main moved to d08ab2f after the merge and was not merged again: verified, the three commits (4758b33, a95ec20, d08ab2f) touch 15 files in components / core / fields / plugin-charts / types and none of this PR's seven; the PR is mergeable: true, state behind. (3) NOT MEASURED check:eager-locale-catalogues and the eager-closure budget: Docs Route Eager Closure Check and Bundle Analysis are success at the head; no performance-budget check-run exists among the 43, so it is unmeasured at the head too, which is not a defect of the diff. (4) Acceptance notes: localize() reading an authored label map en-first is author data, outside the card; the duplicated withSlots is style; the flow-reference picker, icon picker and SchemaForm words are outside the card; the zh rows do keep the separating space before the slotted node ('使用 {value}' 3517, '当前会话不具备 {capabilities},…' 3173).
  • CI at the head 7c35ba6. Initial read 16:42:27Z: 42 check-runs, 28 success, 3 skipped, 11 in progress. Re-poll 1 at 16:47:27Z: 29 success, 3 skipped, 10 in progress. Re-poll 2 at 16:49:55Z: 43 check-runs, 37 success, 3 skipped, 2 failure, 1 in progress (Spec Main Shape Gate). Lint, Type Check, Build & E2E, Test (dist pins) and the changeset gates are success. The two failures are Test (shard 2/8) and its Test roll-up. Cause, from the check-run annotation and the job log: apps/console/src/pages/developer/ApiConsolePage.requestPreset-10591.test.tsx:299, the second it.each case, findByPlaceholderText('/api/v1/...') timed out while the console was still on its boot splash ("Initializing application…", "Connecting to data source"); 1 failed of 6952 tests in that shard, which ran 633 s with 1240 s of import time. This PR touches nothing under apps/console; that test mocks views/metadata-admin and the providers (lines 44–115); the identical wait passed in the two sibling cases of the same file in the same run; shard 2/8 is green at the merge-base 02e6d36 and at main d08ab2f. Judged a timing flake outside this diff, not a defect of it. The head is nonetheless not green: the seat needs a green rerun (the merge queue rebuilds on current main) before ready / automerge.
  • Draft: yes (draft: true).
  • Clause-② no: holds. The four modules' export lines are unchanged in name and type (only InspectorShell's parameter destructuring changed; InspectorShellProps, InspectorReorderButtonsProps and InspectorComboFieldProps are unchanged). PARAM_TYPE_OPTS's element shape changed, but the module is not reachable from the package barrel (src/index.ts re-exports pages and registries, no inspector module; package.json exports only . and ./styles.css), and its one in-repo consumer reads .value. Stored option values and the LOCATION_LABELS key set are unchanged. No accept set moves.
  • Main under the PR's files since the merge-base: no (git diff --stat over the seven paths is empty).
  • Card closure: body line 1 is Fixes #10586; the branch is the claimed claude/issue-10586-inspector-strings-i18n-pr2; no commit message carries a closing keyword.
  • Duplicate keys: none (① item 2).

Implemented-by: claude/issue-10586-inspector-strings-i18n-pr2
Reviewed-by: session_01KUxVUa7e39aNjhkKi1gsoy

VERDICT: PASS

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3060.1 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-CDNJY6-1.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.57KB 6.15KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.17KB 10.58KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 547.22KB 130.96KB
core (index.js) 9.52KB 3.79KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 259.19KB 65.61KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.49KB 14.64KB
plugin-charts (index.js) 76.80KB 21.35KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.60KB 35.41KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.85KB 61.58KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 152.55KB 39.16KB
plugin-gantt (index.js) 169.71KB 41.93KB
plugin-grid (index.js) 216.65KB 59.28KB
plugin-kanban (index.js) 48.35KB 15.08KB
plugin-list (index.js) 115.77KB 28.75KB
plugin-map (index.js) 23.01KB 7.60KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.75KB 8.98KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.77KB 21.99KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 116.21KB 38.14KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: b5f081d6f60de6645e6927555f8cdcc742e223be

Re-review at the PR's new head. Read for this record: card objectui#10586 (body and all 10 comments), the triage 5832133290, the three claims 5832991567 / 5834151314 / 5834971726, the packages/i18n README section "Scope — the engine.* carve-out" (objectui#4662) the claims cite, PR objectui#10678 (body, 7-file list, 6-commit list, check-runs at the head, two polls), the prior at-tier record 5836207934 on 7c35ba6 (as reference only; every judgment below is re-derived), the diff 7baede3745904d929d4afd80bcd5e2314b466072..b5f081d6f60de6645e6927555f8cdcc742e223be through refs/review/pr-10678 and refs/review/main, AGENTS.md 版本号策略 (lines 267–273) and the changeset-presence rule (line 168), .changeset/config.json. The os-dev-report comments were not used as evidence. Zero GitHub writes. Line numbers below are at the head.

① Derived judgments

Binding text. Triage 5832133290: 「route the strings through engine.inspector.* / engine.form.* with keys in every built-in locale; en stays byte-identical」. Claim 5834151314 reads 「keys in every built-in locale」 as the designer catalogue's two built-in locales, because the README records that no engine.* key exists in any of the ten packs (the objectui#4662 carve-out, README lines 257–297). Claim 5834971726 file surface: inspectors/HookDefaultInspector.tsx, inspectors/ActionDefaultInspector.tsx, inspectors/InspectorComboField.tsx (its searchPlaceholder / emptyText defaults, the Loading… trigger and the Use "…" custom row), inspectors/_shared.tsx (its Close / Move up / Move down / Options could not be loaded defaults), their rows in i18n.ts (ENGINE_STRINGS_EN and ENGINE_STRINGS_ZH), pins beside them, one .changeset/10586-…-2.md; ⛔ not packages/i18n/src/locales/.

  1. Re-review facts (git, not the report). The merge-base with refs/review/main is 7baede3, and refs/review/main is exactly 7baede3. The PR's own diff against the new merge-base is byte-identical to its diff against the old one (02e6d36..7c35ba6): both patches have sha256 6c3face2058a21a36988e8d7b293eac14cfbf369ba05db67c5368cabdc13b9d6, 55 hunks, the same seven files, +1213 / −187. The merge commit b5f081d (parents 7c35ba6, 7baede3) brought in four main commits (4758b33, a95ec20, d08ab2f, 7baede3) over 17 files; git diff 7c35ba6 b5f081d over the seven PR paths is 0 bytes and over packages/app-shell/src/views/metadata-admin/ is empty. 7baede3 is PR objectui#10677 (the objectui#10645 test change), and d08ab2f is an ancestor of 7baede3. So the contract below is judged on the same seven files and hunks the previous record judged.

  2. Surface. 7 files: the four claimed components, metadata-admin/i18n.ts (+325 / −0), one new pin inspectors/inspectorStrings.hookAction.i18n-10586.test.tsx (+615), one changeset .changeset/10586-inspector-strings-i18n-2.md (+23). Nothing under packages/i18n/. Right against the claim.

  3. Catalogue rows, i18n.ts. Parsed both tables at the head: EN 1943 single-quoted rows / 1943 unique, ZH 2005 / 2005, 0 duplicate keys in either; 156 new keys in each, and the new EN and ZH key sets are equal: engine.inspector.hook.* 27 (EN 797–823, ZH 3040–3066), engine.inspector.action.* 125 (EN 828–952, ZH 3070–3194), engine.inspector.combo.* 4 (EN 1222–1225, ZH 3514–3517). No pre-existing key was removed and no pre-existing value changed in either table; the older hook.kind (794 / 3038), action.kind and action.close rows are not re-declared. No new zh row equals its en row, and the {slot} name set matches on every pair. Every EN key at the head has a ZH row (0 missing; the 62 ZH-only keys pre-exist in the base, 1849 − 1787). Right against the triage as the carve-out ruling reads it.

  4. en byte-identical, row by row. A code-point exact comparison of every literal the diff removes from the four components against the EN rows at the head: 154 removed literals match a new or reused row exactly (this covers the rows with U+2014, U+2019, U+2026, U+00B7 and U+2265, for example Record · more menu and Tool description (required, ≥40 chars)). The remainder were checked by hand: (a) stored option values (script, primary, action:button, create, text…) are not words and stay as they were; (b) sentences the base spelled as multi-line JSX text, which JSX collapses to single spaces, and whose row carries exactly the collapsed text: action.updateHint (row 150), action.noPlacement (170), the capability sentence (171) and action.pinnedScript (142); (c) slotted rows: pinnedScript at Action 680 renders "Pinned to ", the code element and the tail, the base's three children; scriptBodyHint at 772 wraps the same signature code element; capabilityGate at 879 takes the mono span as {capabilities} and capabilityGateAll "all of them" / capabilityGateOne "it" as {whom}, then capabilityGateSelf at 887 follows the same explicit space; the combo's Use {value} splits at 188 into "Use " and an empty tail around the unchanged quoted mono span (233); removeOption "Remove option {index}" through tFormat (454) gives "Remove option 1" for the base's template literal (tFormat substitutes {name} from its vars, i18n.ts 5300–5309). Reused rows equal the removed defaults byte for byte: engine.form.selectEllipsis "Select…", engine.close "Close", engine.inspector.reorder.up / .down "Move up" / "Move down", engine.form.optionsLoadFailedTitle "Options could not be loaded"; the new combo.loading "Loading…" equals the removed trigger literal. Right.

  5. ActionDefaultInspector.tsx. KeyedOption (69) and localizeOptions (71–73) resolve option labels at render; the six option tables keep every stored value (87–129, 289–292). PARAM_TYPE_OPTS (147–156) keeps its satisfies check over ResolvableParamFieldType; LOCATION_LABELS (202–209) stays a total Record over ActionLocation with keys as values; TARGET_FIELD (221–227) keys the worded placeholders and keeps the sample values object.viewKey and /api/v1/… literal; targetCfg (637–642) still hands ActionTargetField (239–260) plain label / placeholder / hint strings, so the * suffix composition is untouched. withSlots (81–85) is the split-and-interleave shape. ObjectPicker (350) and ParamOptionsEditor (415–420) gain a required locale and receive it (664, 830); both are file-local. The per-input move buttons (807–808) reuse engine.inspector.reorder.up / .down, en identical to the removed aria-labels. tr (506) is the host locale prop, which MetadataDefaultInspectorProps declares (default-inspector-registry.ts 58–59). Left literal on purpose: the four HTTP methods (119–124), status / done sample placeholders, the JS sample body placeholder, the # counter and the arrows. Right.

  6. HookDefaultInspector.tsx. BODY_LANG_OPTS carries labelKey (50–51), resolved through tr at 304; tr (125) is the host locale prop. 27 sites between 215 and 390, including the answered-empty roster sentence (236) and the two body hints (322–323). The priority 100 (295) and the lifecycle event names stay literal: identifiers, not words. Right.

  7. InspectorComboField.tsx. Imports t, useMetadataLocale (50); the three word props are renamed to *Prop (136–138); useMetadataLocale() runs unconditionally after the two useStates (147) and each default is prop ?? t(key, locale) (148–150); the loading trigger (167) and the custom row (188, 233) read the catalogue. ?? keeps the former default-parameter semantics (undefined only), and the props are typed as optional strings, so a caller's own placeholder / searchPlaceholder / emptyText still renders exactly. useMetadataLocale() is useObjectTranslation().language mapped to zh-CN / en-US (i18n.ts 5378–5381), the channel InspectorSelectField's flag already reads (_shared.tsx 415). The placeholder default ("Select…") is outside the claim's enumerated list: same defect class, same signature, in a claimed file; the en text is identical and a caller's placeholder wins (148). Accepted as the named deviation.

  8. _shared.tsx. InspectorShell (60–62): closeLabelProp ?? t('engine.close', locale). InspectorReorderButtons (106–118): the hook is read at 115, before the early return at 116, so hook order does not depend on props; the labels resolve at 117–118. InspectorSelectField (347, 355, 415–416): rosterFailureLabelProp ?? t('engine.form.optionsLoadFailedTitle', locale). Reach, from a per-site tag-span scan of every non-test caller at the head: 21 InspectorShell sites (19 under inspectors/, 2 in studio-design/ObjectGroupInspector.tsx), 19 pass closeLabel and 2 pass hideClose only, so 0 reach the default; 5 InspectorReorderButtons sites, only ObjectFieldInspector 797–802 passes no labels; all 4 roster= sites (ReportDefaultInspector 408 / 504 / 513, ViewColumnInspector 300) pass rosterFailureLabel; 18 InspectorComboField sites, of which 18 reach the emptyText default, 17 the custom row (one passes allowCustom={false}), 15 pass loading, 4 reach the searchPlaceholder default and 3 the placeholder default (all in AppNavInspector: 235 passes placeholder only; 401, 418, 464 pass neither). This reproduces the PR body's reach table exactly. A caller's own text is untouched. Right.

  9. Missing asks. None: every item of claim 5834971726's surface is present. A residual sweep of the four head components (attributes, JSX text, quoted ternaries and option literals, comments stripped) finds only the non-word literals named in item 4 and 5; the two atoms have none.

  10. Not asked. The placeholder default (item 6, accepted); the two file-local helpers; the JSDoc edits in InspectorComboField.tsx (88–95) and _shared.tsx (19–28, 43, 94, 320–325, 393), which document the change in the claimed files. Nothing else.

  11. Existing pins. None edited or deleted: the file list adds one test and modifies no test. The objectui#6538 pin ActionDesigner.paramVocabulary.test.tsx imports PARAM_TYPE_OPTS (59) and reads only o.value (80), so the label to labelKey rename does not weaken it.

  12. New pin can fail. inspectorStrings.hookAction.i18n-10586.test.tsx, 11 cases (hook zh / en, action zh / en, combo zh / en, shared zh / en, caller-label, two lit controls). Lists recounted: HOOK_EN 27 / 27 unique (206), ACTION_EN 121 / 121 (319), COMBO_EN 5 (505), SHARED_EN 4 (553). Negative leg: expectNone (172) over those literals fails on the base by construction, since the base rendered title="Basics" and its siblings unconditionally; the named zhRow samples (160–165) refuse an echoed key (t returns the key when a row is missing, i18n.ts 5267–5269) and an en-equal row, so a fixture that rendered nothing cannot pass vacuously. Reach: the en legs expectAll (167) require every listed literal to render. Caller-label case (584). Lit controls (603–614): ObjectDefaultInspector's designer.object.section.basic on the host locale channel and InspectorSelectField's engine.form.notFound flag on the useMetadataLocale() channel; both keys exist in both tables. Hosts verified at the head: AppNavInspector 401–409 passes only loading to the object combo the pin clicks by its engine.inspector.appNav.object label; ObjectFieldInspector 797–802 passes no reorder labels. Not run here (read-only review): judged from the code, from tsconfig.test.json (src/**/*.test.tsx, so Type Check compiled it) and from the eight green shards in ③.

  13. Round 2 paragraph of the PR body. "merged origin/main 7baede3 (conflict-free; no file of this PR moved; the incoming commits carry the objectui#10645 test change, PR objectui#10677)" — true, item 0. "both card pins Tests 21 passed (21), exit 0; check:i18n-designer-parity exit 0; type-check 30/30 tasks, exit 0" — the dev's own runs, not re-run here; consistent with CI at the head (Type Check and all eight Test shards success), and the parity gate's subject was re-derived independently in item 2 (0 EN keys without a ZH row, 0 duplicates). The earlier body paragraph "origin/main moved to d08ab2f … the branch was not merged again" was true when written and is superseded by Round 2, which merged 7baede3 (an ancestor chain that contains d08ab2f).

② Semver level

'@object-ui/app-shell': patch. AGENTS.md 版本号策略 (268–271): no major in the fixed group, minor / patch evolve independently; line 168: a change under a fixed-group package's src/ owes a changeset, and @object-ui/app-shell is in the fixed group of .changeset/config.json. A behaviour fix with no export, type or accept-set change is patch. Right. Not docs-only (no content/docs/** file in the diff), so the empty-frontmatter rule does not apply.

Changeset prose, sentence by sentence: title "the hook and action inspectors, and the shared inspector defaults, read their words in the designer's locale" — true. "HookDefaultInspector and ActionDefaultInspector rendered their section titles, field labels, placeholders, hints, option labels and accessible names as English literals" — true (the removed lines). "InspectorComboField and _shared.tsx fell back to English defaults wherever a caller passed none: the combo's placeholder, search box, empty state, loading text and "Use" row, and the shell's close label, the reorder pair's names and the roster-failure notice" — true (the removed defaults at base Combo 127–129 and 154 / 218, base _shared 57, 105–106, 318 / 346). "So a zh-CN author read English inside an otherwise Chinese designer" — true. "The two inspectors now read the designer's own string catalogue under engine.inspector.hook.* and engine.inspector.action.*, each row in en and zh, from the locale their host already passes" — true (tr at Hook 125 / Action 506; locale into the two helpers). "The combo's words are new engine.inspector.combo.* rows" — true for search, noMatch, loading, useCustom. "The other defaults reuse existing rows (engine.form.selectEllipsis, engine.close, engine.inspector.reorder.*, engine.form.optionsLoadFailedTitle), and they follow the active language through useMetadataLocale()" — true. "A label the caller passes still wins" — true (?? at Combo 148–150, _shared 62, 117–118, 416; the pin's caller-label case). "The en-US text is unchanged" — true (① item 3). "Stored values, lifecycle event names, HTTP methods, code samples and sample values are not words and stay as they were" — true.

No content/docs/** or README prose is added. The added JSDoc: InspectorComboField.tsx 88–95 says the combo's words read through useMetadataLocale() as InspectorSelectField's default flag does and a caller's own text is kept — true. _shared.tsx 19–28 says the defaults resolve through useMetadataLocale(), the hook widgets.tsx, SchemaForm and ConditionBuilder already read (verified: 19, 8 and 1 useMetadataLocale() calls at the head), and a caller's label always wins — true. 43: the close label defaults to engine.close — true. 94: the reorder pair defaults to engine.inspector.reorder.up / .down — true. 320–325 and 393: the roster notice defaults to engine.form.optionsLoadFailedTitle in the designer's locale, and a passed rosterFailureLabel is kept — true.

③ Boundary flags

  • PR body flags. (1) The placeholder default deviation: judged in ① item 6, accepted. (2) origin/main at d08ab2f not merged again: superseded, ① item 12. (3) NOT MEASURED check:eager-locale-catalogues and the eager-closure budget: Docs Route Eager Closure Check and Bundle Analysis are success at the head; the newest Console Performance Budget comment (5836459884, 17:15Z, after the head was pushed) reads PASS at 3060.1 KB of a 3104.5 KB budget, though it names no sha. (4) Acceptance notes: localize() reading an authored label map en-first is author data, outside the card; the duplicated withSlots is style; the flow-reference picker, icon picker and SchemaForm words are outside the card; the zh rows do keep the separating space before the slotted node ('使用 {value}' 3517, '当前会话不具备 {capabilities},…' 3173).
  • CI at the head b5f081d. Poll 1 at 17:23:41Z: 42 check-runs, 31 success, 3 skipped, 8 in progress (Test shards 1–7, Spec Main Shape Gate). Poll 2 at 17:30:15Z: 43 check-runs, 40 success, 3 skipped, 0 failure, 0 in progress; the skipped three are Test (coverage), Test (coverage shard …/4) and dependabot. Lint, Type Check, Build & E2E, Test and all eight shards, Test (dist pins) and every changeset gate are success. Test (shard 2/8), red at 7c35ba6 on the objectui#10645 test, is green here with PR objectui#10677 in the base. No third poll was needed. NOT MEASURED: the shard job logs themselves (the log download's redirect host is refused by the agent proxy with a 403 on CONNECT), so the per-file line showing the new pin inside a shard was not read; its run is evidenced indirectly, ① item 11.
  • Draft: yes (draft: true), mergeable: true, mergeable_state: blocked.
  • Clause-② no: holds. package.json exports only . and ./styles.css; src/index.ts exports no inspector module (only the inspector-registry functions) and is unchanged in the diff. The four modules' export lines are identical between base and head except InspectorShell's parameter destructuring (its name and InspectorShellProps unchanged; InspectorReorderButtonsProps and InspectorComboFieldProps gain only doc comments). PARAM_TYPE_OPTS's element shape changed (label to labelKey), but the module is not reachable from the barrel and its one in-repo consumer reads .value. Stored option values and the LOCATION_LABELS key set are unchanged. No accept set moves.
  • Main under the PR's files since the merge-base: no — refs/review/main is the merge-base itself.
  • Card closure: body line 1 is Fixes #10586; the branch is the claimed claude/issue-10586-inspector-strings-i18n-pr2; none of the six commit messages carries a closing keyword. PR 1 (objectui#10651) is on main as 531c746.
  • Duplicate keys: none (① item 2).
  • Observation outside the card, not a defect of this diff: DashboardWidgetInspector.tsx 428 passes searchPlaceholder="Search fields…" as an English literal to the combo; that file is not on the card's surface.

Implemented-by: claude/issue-10586-inspector-strings-i18n-pr2
Reviewed-by: session_01KUxVUa7e39aNjhkKi1gsoy

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 25, 2026 17:38
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit d02974d Sep 25, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10586-inspector-strings-i18n-pr2 branch September 25, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants