Skip to content

fix(fields,plugin-detail,plugin-grid,app-shell,i18n): read-only booleans, the boolean list face and the Home greeting follow the locale (objectui#11689) - #11731

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-11689-boolean-greeting-locale
Oct 6, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-11689-boolean-greeting-locale

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11689

Clause-②: yes

Read-only boolean values, the boolean list face and the Home greeting now take their words and punctuation from the language packs. Under zh-CN the record-detail highlights strip read the English word beside a translated label (the card's "Email Verified" sighting), the list face's status badge read "LABEL — Off", and Home joined the night greeting to the name with an ASCII comma and period. All of them now read in the session language; en is unchanged.

The file surface is the claim's, widened by claim amendment 6019561490 (the two family sites seat 1 recorded in 6019099345), plus the producer the measurement found (see "Where the sighting came from").

Read-only boolean words: the existing common.yes / common.no

Surface Before Now
BooleanField readonly branch literal 'Yes' / 'No' useBooleanValueLabel()
FormulaField with returnType: 'boolean' literal useBooleanValueLabel()
renderLookupColumnValue plain-text fallback literal BooleanValueText (same hook; the function cannot call a hook itself, and both callers render what it returns)
record-detail highlights chip (HeaderHighlight, @object-ui/plugin-detail) JSX text Yes / No useBooleanValueLabel(), exported from @object-ui/fields

Why common.yes / common.no and no new key. The packs carry three yes rows. common.yes / common.no is the pair whose meaning is "the value of a boolean": its one existing reader, app-shell's audit-history display, draws a boolean field's old and new value with it. grid.yes / grid.no belong to ObjectGrid's own toolbar bundle (group header, bulk-action dialog), and lookup.yes is the record picker's filter-checkbox caption with no no half. No confirm dialog reads common.yes.

One rule, one home. useBooleanValueLabel (new file packages/fields/src/widgets/booleanValueLabel.tsx) is the only place that maps a boolean to its key; every drawer above calls it. useFieldTranslation's defaults table carries both rows with the en values, so a field rendered without an I18nProvider still says Yes / No.

Where the sighting came from. sys_user lists email_verified among its highlightFields, and the highlights chip spelled the words as JSX text, which is why a plain 'Yes' grep in packages/fields and packages/components did not find it. The record body's own row draws a boolean through BooleanCellRenderer (a checkbox, no word). So the fix also lands in @object-ui/plugin-detail, as the order allows for a producer found elsewhere.

The boolean list face (BooleanCellRenderer): three new keys under fields.boolean

Word Key en zh
status badge for false on a status-named column fields.boolean.offBadge {{label}} — Off {{label}} — 已关闭
completion indicator, true (accessible name) fields.boolean.completed Completed 已完成
completion indicator, false (accessible name) fields.boolean.notCompleted Not completed 未完成

The badge is one interpolated sentence, so a locale can reorder the label and the word. Read through the same hook and defaults table as the Yes / No words (useFieldTranslation). Reuse was considered and refused: packagedAutomation.off is a console page's switch state, dashboard.config.refresh.off the auto-refresh setting, chatbot.toolState.completed an AI tool call's state. None of them names a field value. The zh value follows the pack's existing word for off (packagedAutomation.off).

The grid hands the face the header's label

ObjectGrid handed the cell face the authored field label while its header printed the translated one (resolveFieldLabel). So under a non-en pack the badge sat under a translated header and named the column in the authored language. Every schema-backed path now hands the face the header it already resolved, with no second copy of the label rule: object-shaped columns, string columns, inline-data columns, and the columns generated from the schema. The last is not one of the three sites the amendment names; it is the same one-line label handoff. The authored-label guard stays, so an unlabelled field keeps the face's own fallback.

Greeting joiners: two new keys

home.greetingSeparator (the comma before the name, rendered only when there is a name) and home.greetingEnd (the closing mark). I chose a joiner pair over Trans component interpolation: the markup stays as it is, so the name keeps its own text-primary span and the closing mark its muted one, and no pack carries component markup.

Pack separator end
zh , 。
ja 、 。
ar ، .
en, de, fr, es, pt, ru, ko , .

zh-CN now reads "夜深了,Dev Admin。"; en still reads "Working late, Dev Admin.".

Public surface (the Clause-② reading)

  • Built @object-ui/i18n dist/locales/en.d.ts, base c910630 against head 550037f: five members added, nothing else moved. They are fields.boolean with offBadge: "{{label}} — Off", completed: "Completed" and notCompleted: "Not completed", plus home.greetingSeparator: ", " and home.greetingEnd: ".". The TranslationKeys type derived from en gains the same five.
  • @object-ui/fields: one new export, useBooleanValueLabel, with its type BooleanValueLabel. BooleanValueText stays internal.
  • Changeset .changeset/11689-boolean-greeting-locale.md: minor for fields, plugin-detail, plugin-grid, app-shell and i18n.

Pins (all under a real I18nProvider over the real packs; en and provider-less are the controls)

  • packages/fields/src/__tests__/booleanValueLabel.locale-11689.test.tsx has two parts. The three drawers: zh 是 / 否, en Yes / No, every pack's own common.yes / common.no, provider-less Yes / No. Then BooleanCellRenderer: zh badge 启用 — 已关闭, zh accessible names, en unchanged, every pack, provider-less.
  • packages/plugin-detail/src/__tests__/HeaderHighlight.booleanLocale-11689.test.tsx: zh chips 是 / 否, en and provider-less Yes / No.
  • packages/app-shell/src/console/home/__tests__/HomePage.greetingLocale-11689.test.tsx: the real HomePage at 02:30 local. zh heading 夜深了,Dev Admin。, en Working late, Dev Admin., the name keeps its coloured span, and with no name only the closing mark appears.
  • packages/plugin-grid/src/__tests__/ObjectGrid.cellFaceLabel-11689.test.tsx: object-shaped, string and generated columns. zh badge 启用 — 已关闭 under a 启用 header, en Active — Off.
  • packages/plugin-tree/src/ObjectTree.cellFaces-11686.test.tsx (from objectui#11721, merged here at a58626c): its zh pin is re-judged to the whole translated badge, 启用 — 已关闭.

Ablation (one-shot, at 550037f, four legs, each restore proven)

Each leg went through ablation-replace.mjs, which checks the anchor hit, the marker count and that the blob changed, then restores and proves blob == HEAD with an empty git diff HEAD. After all four legs, git status --porcelain was empty. The tests resolve the packages through the vitest source alias, so no dist step was involved.

Leg Mutation Predicted Observed
1 useBooleanValueLabel returns the English literals zh and every-pack cases red in the three drawers and the chip, en and provider-less green 7 failed, 13 passed (20)
2 greeting separator back to ASCII ', ' only the zh full-width case red 1 failed, 3 passed (4)
3 BooleanCellRenderer badge back to the literal — Off fields zh and every-pack badge cases, three grid zh cases and the tree zh pin red 6 failed, 22 passed (28)
4 grid string and inline paths hand the authored label again only the string-columns zh case red (Active — 已关闭 received) 1 failed, 5 passed (6)

Local verification (head 550037f; CI runs the full farm)

Check Result
pnpm --filter X type-check for i18n, fields, plugin-detail, plugin-grid, plugin-tree, app-shell (after a dependency-closure build of the app-shell and plugin-tree closures) all exit 0
pnpm exec vitest run packages/fields/ 235 files passed, 1 skipped
pnpm exec vitest run packages/plugin-detail/ packages/i18n/ 321 files passed, 1 skipped
pnpm exec vitest run packages/plugin-grid/ packages/plugin-tree/ 211 files passed
the root i18n suites in scripts/__tests__: defaults-population-parity-7904, check-i18n-call-site-keys, check-i18n-en-drift, check-i18n-dead-keys, placeholder-spelling-parity 5 files passed
app-shell, narrowed: console/home/, the defaults-maps mirror, audit-history display, the three AppContent suites that mount HomePage, and RecordDetailView.lookupDependsOn-7190 23 files passed
check:i18n-keys, check:i18n-drift, check:i18n-dead-keys (report-only), check:i18n-designer-parity exit 0
check:new-line-citations (0 new), check:control-bytes, check:changeset-claims (report-only; the one paragraph it names, in the 11659 changeset, stays true), check:pending-changeset-literals, check-changeset-no-major, check-changeset-presence exit 0
check:test-path-roots, check:phantom-deps, check:esm-specifiers, check:unreferenced-sources, check:self-import, check-vi-mock-override-shape exit 0
check-governed-queue-guard.mjs --test over the diff NOT GOVERNED
pnpm --filter X lint (eslint .) for the six packages 0 errors each; warnings are not gated (lint.yml sets no --max-warnings)

The app-shell run is a declared narrowing: the full package has more than a thousand test files, and CI runs them sharded.

Acceptance notes (observed, not changed here, nothing filed)

  • More hard-coded English boolean words outside this surface: ObjectGantt's tooltip boolean branch, PropertyEditor's boolean caption (plugin-designer), and deriveMasterDetail's optionsFor labels for a boolean column (plugin-form). Raw true / false text also appears in ConcurrentUpdateDialog and HistoryTimeline (plugin-detail) and in record-picker (components).
  • The highlights chip and the boolean FormulaField still read the value by truthiness, so a null boolean highlight draws the "No" chip. That is the defect class the readonly BooleanField and BooleanCellRenderer were ruled on; it is not judged here.
  • The ko pack spells common.no 아니오 and grid.no 아니요.
  • The grid's inline-data path prints the authored label in its header, without resolveFieldLabel. The face now matches that header.
  • The grid's generated-columns path hands the face the raw schema def. A def without its own name never reaches the boolean face's status and completion name tests.
  • app-shell's audit-history display keeps its own common.yes / common.no selection through its ctx.t, with its own truthiness.

Generated by Claude Code

claude added 6 commits October 6, 2026 14:24
…1689)

`BooleanField`'s readonly branch, a boolean `FormulaField` and the lookup
column's plain-text fallback spelled 'Yes' / 'No' in code. They now read
`common.yes` / `common.no` through one hook, `useBooleanValueLabel`, whose
provider-less defaults keep the English words.

Claude-Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju
Co-authored-by: Claude <noreply@anthropic.com>
…ing follow the locale (objectui#11689)

The record-detail highlights strip spelled its boolean chip 'Yes' / 'No' as
JSX text; it now reads `useBooleanValueLabel` from `@object-ui/fields`.

The Home greeting joined the greeting and the name with an ASCII ', ' and
closed with '.'. The joiners are two new keys in all ten packs,
`home.greetingSeparator` and `home.greetingEnd`, so zh-CN writes the
full-width comma and full stop and en is unchanged.

Claude-Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju
Co-authored-by: Claude <noreply@anthropic.com>
Brings in objectui#11721 (the tree's cell faces through `BooleanCellRenderer`),
whose zh pin this branch re-judges once the badge word is translated.

Claude-Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju
Co-authored-by: Claude <noreply@anthropic.com>
…olumn label follow the locale (objectui#11689)

The family sites recorded on the card (claim amendment 6019561490):

- `BooleanCellRenderer` spelled its status badge's "Off" and the completion
  indicator's "Completed" / "Not completed" accessible names in English.
  They are three new keys under `fields.boolean` in all ten packs, read
  through `useFieldTranslation` with en defaults.
- `ObjectGrid` handed the cell face the authored field label while the
  header printed the translated one. Each schema-backed path now hands the
  face the header's own resolved label.

The tree's zh pin from objectui#11721 is re-judged to the translated word.

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

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 1 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/11659-console-record-ux.md

  • names HomePage.tsx → packages/app-shell/src/console/home/HomePage.tsx — edited by this change

    The backup-password reminder no longer appears in the user's first session. 「建议设置一个备用密码」 showed on the environment home right after a new user built their first app: the reminder was gated on a home-visit count (quiet on the first home mount, shown on the second), and coming back to home after building is the second mount inside the first sitting. It now stays quiet for 12 hours after the first home visit on the device, so it first shows on a later day's visit. The first-visit record (os:recovery-pw-first-seen) now holds a timestamp; a device that holds the old '1' flag starts the 12 hours over rather than reading it as long ago. When storage is unavailable it stays quiet. RecoveryPasswordReminder moves out of HomePage.tsx into its own module; it is not exported from the package entry, and its other conditions (dismissed, SSO-enforced, has a local password) are unchanged.

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 834c5594b (merge-base with origin/main): 24 file(s) changed outside .changeset/, read against 73 pending declaration(s) that publish a body (75 pending in total). · run

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 331 chunks) 3325.9 KB 3330.4 KB
Main entry chunk (gzip) 154.3 KB 350 KB
Entry file index-C2l7qLhB.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) 17.82KB 6.58KB
app-shell (runtime-config.js) 22.52KB 7.86KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
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.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
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.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
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.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 578.98KB 139.21KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 233.72KB 64.83KB
fields (index.js) 263.36KB 66.69KB
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.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 35.66KB 9.49KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.18KB 11.71KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
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.04KB 3.92KB
plugin-calendar (index.js) 53.39KB 15.52KB
plugin-charts (index.js) 84.26KB 23.05KB
plugin-chatbot (index.js) 198.81KB 47.14KB
plugin-dashboard (index.js) 143.75KB 38.87KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 247.28KB 65.06KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.09KB 45.89KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 238.51KB 65.53KB
plugin-kanban (index.js) 52.17KB 16.37KB
plugin-list (index.js) 116.85KB 29.12KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 38.90KB 11.74KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 90.23KB 22.73KB
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.81KB 3.58KB
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) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
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) 1.13KB 0.65KB
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) 5.78KB 2.70KB
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 (node-slots.js) 7.18KB 2.34KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
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: 550037f593e09c721f8aa8219223facef382995a
Local-runs: none

Reviewed: PR objectui#11731 (Fixes #11689) at 550037f, the net diff against main (merge-base a58626c88; main at 834c559, merge-tree clean per the seat's read, PR mergeable: true), card #11689's body and all five comments (claim 6018076719, seat 1's family sites 6019099345, amendment 6019561490, dev report 6020609294, ACCEPT 6020685348), the PR's 25-file list, and the 42 check-runs on the head. Files were read at the pinned sha through git show, nothing built, run or re-run.

① Derived judgments

Public surface, @object-ui/i18n (entry ., plus the ./locales and ./locales/* subpaths). The entry re-exports en and type TranslationKeys = typeof en (locales/en.ts:4687, index.ts:41, :120). The en.ts diff is additions only: a new fields.boolean sub-object (one boolean: key under fields, at :572, no duplicate) with offBadge, completed, notCompleted, and home.greetingSeparator, home.greetingEnd. All five are absent at the merge-base. The same five land in the other nine packs (ar, de, es, fr, ja, ko, pt, ru, zh; ten packs in src/locales/, matching builtInLocales). The changeset's "Widened public surface" paragraph names exactly these five and nothing else moved on en. RIGHT.

Public surface, @object-ui/fields (entry ., dist/index.d.ts). index.tsx:4437 adds one named re-export: useBooleanValueLabel and type BooleanValueLabel. Nothing else from widgets/booleanValueLabel.tsx reaches the entry: there is no export * from that module, no widgets barrel, and the two export *-ed widgets that import it (BooleanField.js, FormulaField.js) do not re-export it; a tree grep finds BooleanValueText only at its definition and in lookupColumnDisplay.tsx. useFieldTranslation and FIELD_DEFAULTS stay internal (imported at index.tsx:33, never exported). renderLookupColumnValue is not on the entry, and its declared return type was already React.ReactNode, so the boolean branch now returning a node widens nothing; its two callers (LookupField.tsx:1252, a JSX child; RecordPickerDialog.tsx:888, a React.ReactNode cell) render what it returns. The paragraph names exactly the hook and its type; BooleanValueText does not reach an entry. RIGHT.

Published behaviour, read against the head tree.

  • BooleanCellRenderer (on the fields entry; mounted by grid, kanban, gallery, report, tree, detail and dashboard, every call site a JSX mount or React.createElement, none a plain function call) now calls useFieldTranslation() first, before its early return, the shape its hook-bearing siblings (NumberCellRenderer, DateCellRenderer, ...) already use. Its three words read fields.boolean.* with en-valued FIELD_DEFAULTS rows for a provider-less render, and the { label } interpolation through the safe t is the shape lookup.remove already uses. RIGHT.
  • BooleanField (readonly), FormulaField (returnType: 'boolean', truthiness preserved through Boolean(value)) and the lookup plain-text fallback read common.yes / common.no, which exist in all ten packs at the merge-base (zh 是 / 否). Both widgets place the hook before their early returns. RIGHT.
  • HeaderHighlight (plugin-detail entry :132) reads useBooleanValueLabel from @object-ui/fields, a dependency the package already declares. RIGHT.
  • ObjectGrid: all four schema-backed paths hand the face the header binding in scope (:3464, :3728, :3927, :4065); A, B and D resolve it through resolveFieldLabel, C (inline-data) prints the authored label, so there the handoff changes nothing, which the dev's acceptance note records. fieldMeta reaches only the cell-face mount (:791), so no other reader of the label moves. The authored-label guard is kept on all four. RIGHT.
  • HomePage (app-shell entry :292): t is useObjectTranslation(), the defaultValues equal the en pack, and no createSafeTranslation map in app-shell carries home.*, so the defaults-maps mirror has nothing new to hold. RIGHT.

Accept sets. None move: the diff changes which words and marks are drawn, not what any surface accepts or returns as data. RIGHT (the changeset claims no accept-set change).

The three behaviour bullets, sentence by sentence.

  • Booleans: the four surfaces named are the four changed, and the keys pre-exist (holds); zh shows 是 / 否, en Yes / No (holds, pinned under a real I18nProvider); provider-less stays Yes / No through the new FIELD_DEFAULTS rows (holds, pinned).
  • The boolean list face: the three English literals are the three removed lines (holds); all three come from the packs (holds, ten packs); the grid handed the face the authored label while its header printed the translated one (holds on paths A, B, D; path C printed the authored header, noted); the face now receives the header's label (holds on all four).
  • Greeting: two new keys, zh , / 。, ja 、 / 。, ar ، / . (holds); en , / . and the text-primary span kept (holds, pinned).

Card Done-when. The boolean words and the joiners come from keys present in every locale (five new in ten packs; common.yes / common.no already in ten). Pins: zh 是 / 否 and full-width punctuation, en unchanged. Met.

File surface against the claim as amended and accepted. 25 files. Claim 6018076719: BooleanField.tsx, FormulaField.tsx, lookupColumnDisplay.tsx, HomePage.tsx, the ten packs, useFieldTranslation.ts (the defaults rows those packages read), the tests beside them, the changeset. Amendment 6019561490: fields/src/index.tsx (BooleanCellRenderer), ObjectGrid.tsx (three sites). ACCEPT 6020685348: HeaderHighlight.tsx and its test, the ObjectGrid generated-columns path, ObjectTree.cellFaces-11686.test.tsx. The one new path no surface list spells, widgets/booleanValueLabel.tsx, is named in the ACCEPT's own reading and in the changeset; it is the shared home of the claimed sites' word, sits in the claimed widgets directory, and as a new path collides with no open PR. Inside; no breach.

② Semver level

Changeset .changeset/11689-boolean-greeting-locale.md: minor for @object-ui/fields, @object-ui/plugin-detail, @object-ui/plugin-grid, @object-ui/app-shell, @object-ui/i18n.

  • objectui never declares major (AGENTS.md §版本号策略; check-changeset-no-major.mjs), and the maintainer ruling of 2026-09-04 grades a Clause-②: yes at minor or above.
  • @object-ui/i18n widens (en and TranslationKeys gain five keys) and @object-ui/fields widens (useBooleanValueLabel, type BooleanValueLabel): minor is required for both.
  • @object-ui/plugin-detail, @object-ui/plugin-grid, @object-ui/app-shell change the rendered behaviour of published components (HeaderHighlight, ObjectGrid, HomePage) and add no name; minor sits inside the ruling and is the ceiling the repo permits, and the 39-package fixed group takes the highest bump regardless. Fits.
  • No narrowing: no name or key removed, no type narrowed (renderLookupColumnValue was already React.ReactNode and is off the entry), en rendering byte-identical by the pins.
  • Changeset Bump Policy, Changeset Declaration, Changeset Fixed Group Check, Changeset Overwrite Report and Changeset Claim Re-read: success. The re-read's one paragraph (.changeset/11659-console-record-ux.md, RecoveryPasswordReminder moved out of HomePage.tsx) stays true: this diff touches only the heading's two joiners.

Clause-②: yes (widening)

③ Boundary flags

Dev report 6020609294: open_questions is empty; seven deviations, each answered here.

  1. HeaderHighlight.tsx and its test, past the amended claim: recorded by the ACCEPT; the measured producer of the card's sighting; the order allows a producer fix in another package. Answered.
  2. ObjectGrid generated-columns path as a fourth handoff site: recorded by the ACCEPT; confirmed at :4065 as the same one-line handoff with the authored-label guard. Answered.
  3. origin/main a58626c (objectui#11721) merged in, plugin-tree's zh pin re-judged to 启用 — 已关闭: the amendment foresaw it; the pin keeps every prior assertion and gains one. Answered.
  4. Narrowed app-shell vitest set and package-level eslint: CI owns the full farm; Lint is success; the Test shards and Type Check are the landing check (below). Answered.
  5. Commit trailers in objectui AGENTS.md's model-free form, PR footer in the session-URL form: a repo convention, not a contract item; nothing in ① or ② turns on it. Answered, not escalated.
  6. Three lock queue timeouts on the first type-check, retried same-slot: nothing skipped; the gate table at 550037f is complete. Answered.
  7. Two ungated eslint warnings (react-refresh/only-export-components in booleanValueLabel.tsx, an as any in the HeaderHighlight test): lint.yml sets no --max-warnings, Lint is success, and BooleanValueText stays internal so the co-location has no surface effect. Answered.

Six out_of_scope_findings: none cites a contract or names a public door, none alters ① or ②; the seat holds them as acceptance notes and the first is for the family closing card. Not escalated.

Check-runs on 550037f at this read: 42, of which 29 success, 3 skipped, 0 failed, 10 in progress: Spec Main Shape Gate, Test (shard 1/8) through Test (shard 8/8), Type Check. ①, ② and ③ are judged on the diff and the card; those ten runs' convergence is the seat's landing check.

Implemented-by: claude/issue-11689-boolean-greeting-locale
Reviewed-by: session_01FngvPpdrnhHMdHHq6vwwju

VERDICT: PASS


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

i18n: read-only booleans render "Yes"/"No" in every locale, and the home greeting joins with an ASCII ", " and "." under zh-CN

2 participants