Skip to content

fix(components): RefreshIndicator requires its accessible name; four views pass a translated one (objectui#10580) - #10635

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-10580-refresh-indicator-translated-name
Sep 25, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-10580-refresh-indicator-translated-name

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Fixes #10580

Clause-②: yes

RefreshIndicator's published ariaLabel prop loses its English default ("Refreshing") and becomes required, so a caller that passes no name is a type error instead of an English accessible name. This is the "declaration enforces" option the triage allowed on this PR (triage comment 5831898614), taken by the seat's claim (5832044126). The four views that passed no name now pass their translated one. It is the objectui#3546 class: text a user meets that bypasses the translation layer.

What changes

  • packages/components/src/custom/refresh-indicator.tsx: ariaLabel: string is required, the = "Refreshing" default is gone, and the docblock says why.
  • ObjectGrid passes t('grid.refreshing') and ListView passes t('list.refreshing'): the same keys, through the same hooks, that their pull-to-refresh text already reads. No new grid or list key.
  • ObjectChart passes t('chart.refreshing'). The t comes from the useObjectTranslation() call the file already makes for language. It is deliberately not the file's tt (useSafeTranslate), because tt takes an English fallback string at every call, and the ruling is no English fallback anywhere.
  • ObjectDataTable passes t('dashboard.refreshing') from the useObjectTranslation() call it already makes.
  • packages/i18n/src/locales/*.ts: chart.refreshing in the chart block and dashboard.refreshing at the dashboard block's top level, each placed after that block's loading key, in all ten packs. Each pack's value is the translation its own grid.refreshing already carries.
  • .changeset/10580-refresh-indicator-translated-name.md: minor for @object-ui/components, with the break stated as BREAKING (this repo marks its own breaking changes minor), and patch for plugin-grid, plugin-list, plugin-charts, plugin-dashboard and i18n. The changeset prose states the contract change, so a contract review is owed.

Caller census (H4)

git grep -n 'RefreshIndicator' over the whole tree, re-run on the merged head 641ed9f, finds five render sites. After this PR every one passes a translated name: ObjectGrid, ListView, ObjectChart, ObjectDataTable, and ObjectGantt, which already passed t('gantt.aria.refreshing') and is unchanged. Before this PR no other package, app, example, docs snippet or test rendered it. RefreshIndicatorProps has no other code reader; its one other mention is a docblock in MetricWidget.tsx. No docs page renders the component, so check:doc-snippets and check:doc-types have nothing of it to read and were not run. The sibling objectstack checkout has zero RefreshIndicator hits. The merge of origin/main brought in no new render site.

H5

ariaLabel="" still type-checks, because the prop is a plain string. As ruled, no runtime check was added: the type is the gate.

Pins

File What it holds
packages/components/src/__tests__/refresh-indicator-required-name-10580.test.tsx TYPE: RefreshIndicator with no ariaLabel sits under @ts-expect-error, checked by the package's tsc -p tsconfig.test.json. DOM: the bar is named with exactly the passed string, and a caller that casts past the type gets no aria-label at all, so there is no English fallback left.
packages/plugin-grid/src/__tests__/ObjectGrid.refreshIndicatorLocale-10580.test.tsx de: the bar is named Wird aktualisiert… during a bus-driven re-read. en control: Refreshing….
packages/plugin-list/src/__tests__/ListView.refreshIndicatorLocale-10580.test.tsx zh: 刷新中… during a toolbar-refresh re-read. en control: Refreshing….
packages/plugin-charts/src/ObjectChart.refreshIndicatorLocale-10580.test.tsx zh: 刷新中… during a bus-driven re-read. en control: Refreshing….
packages/plugin-dashboard/src/__tests__/ObjectDataTable.refreshIndicatorLocale-10580.test.tsx ja: 更新中… during a filter-change re-read. en control: Refreshing….

In every view pin the second query is held open by hand, so the bar is read while the query really is in flight. ObjectGantt.refreshInPlace-7237.test.tsx was run unchanged and stays green.

Reverse verification (each leg ran from a committed state, with trap restore and restore proven by hash)

  1. Base leg. The implementation files (the component, the four callers, the ten packs) were checked out at BASE adeecd6, with the pins taken from HEAD 7a1b619. The 5 pin files then gave exit 1, Tests 9 failed | 3 passed (12). The failure dump reads aria-label="Refreshing" on the progress bar. On-disk proof of the mutation: the default literal counted 1 and the grid caller's name counted 0. After the restore (git checkout HEAD --), git diff HEAD was 0 bytes and all 15 paths were hash-equal to their HEAD blobs. The same 5 files at HEAD gave exit 0, Tests 12 passed (12).
  2. Type leg. ariaLabel?: string was written back on disk (optional count 1, required count 0). tsc -p tsconfig.test.json in packages/components then gave exit 2, error TS2578: Unused '@ts-expect-error' directive. at the pin. After the restore it gave exit 0. --listFiles shows the pin file in that program (1 hit).
  3. Consumer leg (proves the plugins read the rebuilt dist/*.d.ts, whose refresh-indicator.d.ts declares ariaLabel: string;). With ObjectGrid's ariaLabel dropped on disk, tsc --noEmit in packages/plugin-grid gave exit 2, TS2741: Property 'ariaLabel' is missing in type '{ active: boolean; }' but required in type 'RefreshIndicatorProps'. It was restored hash-equal.

Gates

Pre-merge head b240863:

  • pnpm exec vitest run --maxWorkers=2 packages/plugin-grid/: exit 0, 157 passed (157) files, 1489 passed tests.
  • … packages/plugin-list/ packages/plugin-charts/: exit 0, 172 passed (172), 1982 passed.
  • … packages/plugin-dashboard/ packages/i18n/: exit 0, 201 passed (201), 2424 passed.
  • … packages/components/src/custom/ plus the components pin plus ObjectGantt.refreshInPlace-7237.test.tsx: exit 0, 2 passed (2), 11 passed. src/custom/ holds no test file.

Merged head 641ed9f, after the merge of origin/main 6516320, which moved all ten packs and packages/components:

  • pnpm exec vitest run --maxWorkers=2 packages/i18n/ plus the 5 pins plus the gantt pin: exit 0, 77 passed (77), 1197 passed.
  • pnpm turbo run type-check --filter @object-ui/components --filter @object-ui/i18n --filter @object-ui/plugin-grid --filter @object-ui/plugin-list --filter @object-ui/plugin-charts --filter @object-ui/plugin-dashboard --filter @object-ui/plugin-gantt --concurrency=2: exit 0, 22 successful, 22 total, with all 7 type-check tasks executed (cache miss).
  • pnpm check:i18n-keys, check:i18n-drift, check:i18n-dead-keys (no refreshing candidate), check:control-bytes, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:new-line-citations, check:changeset-claims, check:pending-changeset-literals, changeset:check, node scripts/check-changeset-presence.mjs: all exit 0.
  • pnpm exec eslint over the 20 changed .ts/.tsx files: exit 0, 0 errors.

Lint, narrowed and declared. The repo-wide pnpm lint was not run locally; CI owns it. Evidence that the narrowing excludes nothing: (1) eslint.config.js applies to **/*.{ts,tsx}, and every one of this diff's 20 .ts/.tsx files is in that population; (2) --format json reports 20 files linted, 0 errors; (3) the config has no parserOptions, projectService or project: (0 hits) and no import plugin (0 hits), so linting is not type-aware and this diff cannot move the verdict of any file it does not touch.

  • Round 2: the branch merged origin/main 4a3d500 as 1a89050 (merge only; this PR's own diff is unchanged at 21 files, +543 / −8). There, the plugin-list suite plus the five pins and the ObjectGantt 7237 pin (104 files, 1166 tests), and the seven type-checks, re-ran green. The merge-queue eviction at 13:55Z was the objectui#10645 test, which is not this PR's (os-dev-report round 2, comment 5834284916).
  • Round 3: merged origin/main 7baede3 (it carries PR objectui#10677, the change for objectui#10645) at a6512a7, with no conflicts; the five pins, the ObjectGantt 7237 pin and the seven type-checks re-ran green there (os-dev-report round 3, comment 5836351479).

Acceptance notes

  • Hosts with no i18next instance. A throwaway probe, not committed, rendered ObjectChart with no I18nProvider: the bar is named chart.refreshing, the key itself. ObjectDataTable reads its name the same way (useObjectTranslation().t) and was not measured separately. Grid, list and gantt still resolve English there through their own createSafeTranslation defaults maps. This is what "no English fallback" means at the two new call sites. The console mounts I18nProvider in apps/console/src/main.tsx, so it does not reach the console. The changeset says so.
  • RefreshIndicator spreads ...props after aria-label, so a caller's own aria-label attribute still overrides ariaLabel. That was true before this PR and is unchanged.
  • The comment on the chart block in en.ts still describes the pack as budgeted inside the framework chunk. scripts/check-eager-closure-budget.mjs now gives the pack its own i18n-locale-en ceiling. The comment has drifted and is not touched here.
  • In de, grid.refreshing reads Wird aktualisiert… while list.refreshing reads Aktualisiere…. The two new keys use the former. This is polish and is not touched here.

Generated by Claude Code

…views pass a translated one (objectui#10580)

`RefreshIndicator`'s `ariaLabel` loses its English default ("Refreshing")
and becomes required, so a caller that names nothing is a compile error.
ObjectGrid and ListView pass their existing `grid.refreshing` /
`list.refreshing`; ObjectChart and ObjectDataTable pass new
`chart.refreshing` / `dashboard.refreshing` keys, added to all ten packs.

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

⚠️ 20 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/4730-retire-dead-locale-key-batch.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    • Superseded twin vocabularies. cellRender.* and rowAction.* duplicated a grid.* vocabulary that won. RowActionMenu.tsx is fully i18n-wired and reads grid.openMenu / grid.edit / grid.delete; ObjectGrid.tsx reads grid.empty / grid.yes / grid.no / grid.systemFields. The twins had no reader on either side. - Labels that outlived their control. calendar.agenda labelled a view mode objectui#5740 retired from CalendarViewMode (now 'month' | 'week' | 'day'). home.quickActions.createApp*, layout.systemNav.createApp, actionDialog.defaultActionTitle / .ok and grid.bulk.selectPlaceholder sit in namespaces whose consumers are live and wired but demonstrably read other siblings. - Surfaces that left the product. map.* is the strongest form: @object-ui/plugin-map declares no @object-ui/i18n dependency and contains no t() call at all, so it cannot consume a locale string. home.stats.* and recordDetail.viewersTooltip name surfaces nothing renders.

.changeset/5876-one-lookup-predicate.md

  • names ObjectDataTable.tsx → packages/plugin-dashboard/src/ObjectDataTable.tsx — edited by this change

    computeLookupExpand in ObjectDataTable.tsx carried its own isLookup, byte-identical to the exported isLookupType in recordFields.tsx after objectui#5692 pointed both at @object-ui/core's EXPANDABLE_FIELD_TYPES. Nothing kept them aligned: a future edit to either — a member added, the retirement gate moved — would have re-forked the $expand decision from the predicate whose docblock claims to drive it. computeLookupExpand now calls isLookupType, which gains its first production consumer, and the module no longer imports the shared family or the retirement gate at all.

.changeset/6597-retire-fieldmeta-referenceto.md

  • names plugin-grid/src/ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    No authoring story survived the search either. ObjectGrid's own relational-meta pass-through (applyRelationalMeta, plugin-grid/src/ObjectGrid.tsx) copies reference_to / reference / display_field / etc. from the SCHEMA field def only, at all three of its call sites — never from an authored column override. No doc, example, or fixture in this repo shows a table column pinning a lookup's target away from what its schema field already says. Under the maintainer's standing startup-stage rule (2026-08-27: deprecated/alias spellings retire immediately, no transition windows), no measured demand selects withdraw.

.changeset/6661-app-launcher-nav-menu-renderers.md

  • names en.ts → packages/i18n/src/locales/en.ts — edited by this change

    Three new strings — the launcher's and the menu's accessible names, and the menu's empty state — are declared under console.nav in en.ts and its nine sibling packs. An inline defaultValue alone is not a fix: it renders English at one call site and leaves the string untranslatable everywhere (objectui#3517).

.changeset/7070-no-invented-gantt-date-fields.md

  • names plugin-list/src/ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    • app-shell/src/views/ObjectView.tsx — the console object page. The inline branch becomes ganttViewOptions, the sibling of calendarViewOptions and timelineViewOptions: the declared block spread whole, title floored at 'name', no date field invented. - plugin-list/src/ListView.tsx — the render branch AND the capability gate. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.

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

  • names ObjectDataTable.tsx → packages/plugin-dashboard/src/ObjectDataTable.tsx — 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/7499-gantt-non-axis-floors-omitted.md

  • names plugin-list/src/ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    • plugin-list/src/ListView.tsx — the object-gantt render branch. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.

.changeset/7762-object-grid-export-options-bare-array-refusal.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    What was measured, on this branch's base. The mirror declared NO exportOptions member at all, and BaseSchema is .passthrough(), so ObjectGridSchema.safeParse({ type: 'object-grid', objectName: 'accounts', exportOptions: ['csv', 'xlsx'] }) returned success: true with the array back VERBATIM — as did { formats: ['csv', 'pdf'], compression: 'gzip' }. Nothing on the render path parses, and ObjectGrid.tsx reads schema.exportOptions?.formats and only that, so the authored array then lost SILENTLY to the ['csv', 'json'] default: the useEffect that warns about dropped formats reads .formats too and returns early when it is absent, while !!schema.exportOptions kept the export button on screen. An author declared ['csv', 'xlsx'] and got csv/json with no error, no warning and no console line. The two authoring faces disagreed in the direction opposite to objectui#6956's: the TypeScript interface already declared the object form only, so TS refused what zod admitted.

.changeset/7773-kanban-adapter-groupfield-write.md

  • names ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    Who is NOT affected — the boundary is node-local. Every VIEW-LEVEL groupField read is untouched and still live: it is a legacy alias of the spec's groupByField on the kanban view config, mapped by normalize-list-view.ts, and both adapters still resolve lanes through it (ObjectView.tsx's kanbanCfg.groupField ||, ListView.tsx's groupByField || groupField). Authoring options.kanban.groupField on a list-view or object-view keeps working exactly as documented in packages/plugin-list/README.md. groupField is dead only on the generated object-kanban NODE.

.changeset/7780-object-kanban-record-source.md

.changeset/8650-chart-foreign-dialect-retired.md

  • names ObjectChart.tsx → packages/plugin-charts/src/ObjectChart.tsx — edited by this change

    Also deletes six (schema as any) casts that the published declarations had already made unnecessary — colors, categoryColors and categoryOrder on ChartRenderer, and colors, compareTo and series on ObjectChart (the objectui#8327 bucket-(b) class: declared, then read through a needless cast). No behaviour changes with them; ObjectChart.tsx now has no (schema as any) read left at all.

.changeset/8885-object-chart-drilldown-title-compareto.md

  • names ObjectChart.tsx → packages/plugin-charts/src/ObjectChart.tsx — edited by this change

    ObjectChart.tsx reads all three off schema, and until now neither published copy declared any of them: not the TS interface (packages/types/src/objectql.ts) and not the zod mirror (packages/types/src/zod/objectql.zod.ts). They rode BaseSchema's index signature / .passthrough() and arrived unvalidated. drillDown was the sharpest case — this component's registry inputs advertise it to the designer palette, and @objectstack/spec publishes ChartDrillDownSchema for exactly this carrier, so an author was offered a key that neither published shape mentioned.

.changeset/8990-object-kanban-groupby-optional.md

  • names packages/plugin-list/src/ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    • packages/plugin-list/src/ListView.tsx generates the node as groupBy: laneField. objectDef loads asynchronously, so laneField is undefined on every load until it lands, and stays undefined whenever the object offers no stageField hint and none of status / stage / state / phase. The renderer serves that node; both published faces refused it. - content/docs/utilities/data-objectstack.mdx documents an object-kanban node that is exactly { type, dataSource }, with no groupBy. ⚠️ This one is weaker and is cited for what it is: that fragment is still refused after this change, at RECORD_SOURCE_REQUIRED, because dataSource is not a rung of the record-source ladder. It shows a lane-less board is a documented authoring; it is not a document this change admits.

.changeset/9722-bulk-executor-datasource-face.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    • ObjectGrid.tsx → RecordDetailPanel: removing it type-checks GREEN. It was paying for nothing — both sides already declare the same DataSource | undefined from @object-ui/types. Removed outright. - ObjectGrid.tsx → BulkActionDialog: removing it reddens, and it was paying for TWO things at once — the optional-vs-required arm (the grid declares dataSource?: DataSource, the dialog demands one) AND the bulk-door contravariance. Only the first still needs erasing, so it is now a non-null assertion: today's runtime behaviour is preserved exactly, and any future drift of the face reddens at that site instead of passing silently.

.changeset/9853-grid-non-positive-page-size.md

  • names ObjectGrid.tsx → packages/plugin-grid/src/ObjectGrid.tsx — edited by this change

    Refuse a non-positive pageSize at all three of ObjectGrid.tsx's read points, instead of giving two different answers for one authored value (objectui#9853).

.changeset/dashboard-expandable-family-convergence-5692.md

  • names ObjectDataTable.tsx → packages/plugin-dashboard/src/ObjectDataTable.tsx — edited by this change

    plugin-dashboard's two private copies of the reference-bearing field family now read @object-ui/core's published EXPANDABLE_FIELD_TYPES instead of restating it — LOOKUP_TYPES in recordFields.tsx and the inline disjunction inside computeLookupExpand in ObjectDataTable.tsx. Both carry the identity pin the already-converged consumers carry, so a member-identical private copy fails rather than quietly re-forking the table.

.changeset/humanize-label-single-home-5444.md

  • names ObjectChart.tsx → packages/plugin-charts/src/ObjectChart.tsx — edited by this change

    humanizeLabel turns a stored value into a display string when nothing else resolves it — an option with no declared label, an object name, a chart axis member. It existed twice, byte for byte: once in @object-ui/fields (read by plugin-grid, plugin-gantt, plugin-detail and by that package's own renderers) and once as a deliberate local copy in plugin-charts' ObjectChart.tsx, whose comment said it was there "to avoid a dependency on @object-ui/fields".

.changeset/list-user-actions-collision-5398.md

  • names ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    The harvest now reads the object block only. Both userActions read sites in ListView.tsx carry a comment naming the collision, and __tests__/ListView.userActionsCollision.test.tsx pins each clause of it: the two shapes, a producer that manufactures the view one, the harvest's blindness to it, and the projection that must keep the object's operand with a toolbar block — or an empty block — present on the view.

.changeset/listview-comment-pair-4559.md

  • names ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    Two comment corrections in ListView.tsx (objectui#4559, objectui#4966). No runtime behaviour changes and the emitted bundle is byte-identical; the published .d.ts does change, which is why this is a patch rather than an empty frontmatter.

.changeset/objectchart-schema-anchor-7946.md

  • names ObjectChart.tsx → packages/plugin-charts/src/ObjectChart.tsx — edited by this change

    • ObjectChartProps.schema is ObjectChartSchema; the published .d.ts no longer says props: any. ObjectChartProps is exported. - The four keys are declared on BOTH copies, with value types taken from their READ sites (ChartRendererProps for xAxisKey / series, ObjectChart.tsx for filter) rather than copied from any producer's literal — except where @objectstack/spec already owns the shape, which is aggregate: that one is declared BY REFERENCE as ChartAggregate / ChartAggregateSchema, so the authoring door here and at the react-page publish gate are one shape and cannot drift into two dialects. - colors converges: the zod mirror has declared it since objectui#3913 and the TS interface did not, a drift no ratchet could see because a mirror-only key is in neither of the parity guard's two difference ledgers.

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): 20 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) 3050.4 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-msSzkmj-.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) 546.98KB 130.82KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 260.13KB 65.84KB
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.40KB 14.61KB
plugin-charts (index.js) 74.99KB 20.92KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.54KB 35.39KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.96KB 61.65KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 149.07KB 38.02KB
plugin-gantt (index.js) 169.62KB 41.91KB
plugin-grid (index.js) 215.41KB 58.93KB
plugin-kanban (index.js) 48.26KB 15.04KB
plugin-list (index.js) 114.64KB 28.32KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.67KB 8.95KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.84KB 21.96KB
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: 641ed9fa58802e7419e18f635530356914428f75

Reviewed read-only: card objectui#10580 (body and its three comments: triage 5831898614, claim 5832044126, os-dev-report 5833029249, the last read but not used as evidence), PR objectui#10635 body, its REST file list, the check-runs at its head, AGENTS.md 版本号策略 and the changeset rule at :168, .changeset/config.json, and the code via refs/review/pr-10635 against merge-base 6516320624b9376cba03914d1a63cc3d9a9a5606. Zero GitHub writes.

Diff shape, measured by git: 21 files, +543 −8; 1 changeset added, 1 component source edited, 4 caller sources edited, 10 locale packs edited (+2 each), 5 pin files added. No file is deleted; no existing test file is touched. The REST file list is the same 21 paths. The diff consumes no @objectstack/spec symbol (no spec import is added anywhere), so no spec read was owed.

① Derived judgments

Binding text applied, quoted verbatim: triage 5831898614 「each caller passes its translated name (grid and list reuse grid.refreshing / list.refreshing; chart and data table get keys in every locale). Removing the English default (making ariaLabel required) is the declaration-enforces option and may ride the same PR with Clause-②: yes.」; claim 5832044126 「RefreshIndicator's published ariaLabel prop loses its English default and becomes required」, pin 「each of the four views names its progress bar in the active locale (a non-English control), and ObjectGantt is unchanged」; and the claim's bound constraint 「⛔ No English fallback anywhere, and ⛔ no ?? 'Refreshing' in a caller」.

  1. Published surface: ariaLabel optional-with-English-default becomes required, no default. packages/components/src/custom/refresh-indicator.tsx:45 now reads ariaLabel: string (merge-base: ariaLabel?: string); the destructure at :57-62 has no = "Refreshing" (merge-base had it); :77 still renders aria-label={ariaLabel} on the role="progressbar" at :75. RIGHT: this is exactly the triage's declaration-enforces option, taken with Clause-②: yes as the triage allows. The accepted set of props narrows (a caller with no ariaLabel is now a type error), which is the intended enforcement, not a weakening. Judged as a published-surface change: RefreshIndicator is exported from @object-ui/components (all five plugins import it from that package), and RefreshIndicatorProps has no other code reader in the tree (census at head: its only other mentions are a docblock at packages/plugin-dashboard/src/MetricWidget.tsx:212 and a CHANGELOG line), so no Omit/Partial consumer breaks.

  2. Docblock rewrite at refresh-indicator.tsx:38-44. RIGHT: the claim's file surface names 「and its docblock」. Sentence check: 「Accessible name of the progress bar, rendered as its aria-label」 true (:77); 「Required, with no default」 true (:45, :59); 「An English default here reached every non-English screen-reader user through the callers that passed nothing」 true against the merge-base (four callers passed nothing).

  3. ObjectGrid passes t('grid.refreshing') at packages/plugin-grid/src/ObjectGrid.tsx:5684. RIGHT: the triage says grid reuses grid.refreshing. The t is the one from useGridTranslation() at :1314 (no shadowing between :1314 and :5684, grep-checked), the same createSafeTranslation hook whose t('grid.refreshing') already names the pull-to-refresh text at merge-base :5690. No new grid key, as the triage asked. The hook's English defaults map at :324 is pre-existing at the merge-base; the diff adds no fallback to it.

  4. ListView passes t('list.refreshing') at packages/plugin-list/src/ListView.tsx:4851. RIGHT, same reasoning: t from useListViewTranslation() at :1089 (no shadowing), the key already used at merge-base :4189; defaults map at :898 pre-existing. The card's cited site (~4818) is stale by 30 lines against the merge-base; the seat found the real line.

  5. ObjectChart passes t('chart.refreshing') at packages/plugin-charts/src/ObjectChart.tsx:1521, with t newly destructured from the file's existing useObjectTranslation() call at :558 (merge-base :553 destructured only language). RIGHT against the binding text: the file's other translator tt = useSafeTranslate() (:544) takes (keyOrKeys, fallback: string) and returns string (packages/i18n/src/useSafeTranslation.ts:95), so every tt call must carry an English literal, which the bound constraint forbids for this string. Convention check the brief asked for: at the merge-base every other user-visible string in ObjectChart.tsx reads tt with an English fallback (:1178, :1281, :1389-1396, :1477); NO other user-visible string there reads bare t. So this call is the first provider-less-key string in that file. That is a real consequence, not a mistake: it is what 「no English fallback anywhere」 requires, and the changeset states it (see ②). The added comment at :553-557 is true sentence by sentence (the tt fallback requirement, and the pack being the only source of the name).

  6. ObjectDataTable passes t('dashboard.refreshing') at packages/plugin-dashboard/src/ObjectDataTable.tsx:1055, from the file's existing const { t } = useObjectTranslation() at :623. RIGHT. Convention check: this file already reads bare t at the merge-base for dashboard.noDataSourceFor (:624), but guards it with a hand-rolled English fallback at :612-625; the new call drops that guard, which is the bound constraint's demand. The added comment at :1053-1054 (「no English default here, unlike noDataSourceLabel above」) is true.

  7. Provider-less behaviour, judged against the binding text. With no i18next instance at all, useObjectTranslation().t is react-i18next's notReadyT wrapped by packages/i18n/src/provider.tsx:934-950, which with no defaultValue hands back the key: the chart's bar is then named chart.refreshing and the table's dashboard.refreshing, while grid and list still resolve English from their pre-existing createSafeTranslation defaults maps (useSafeTranslation.ts:76-78 returns fallbackT only when t(testKey) === testKey, i.e. no instance). The diff adds no English fallback and no ?? 'Refreshing' anywhere: the only literal English Refreshing strings in the diff are the en pack values and test expectations. The asymmetry between the pairs is inherited from the hooks the triage told the seat to reuse, not introduced here. The PR's own option B (tt(key, 'Refreshing…')) would breach the bound constraint, so option A as shipped is the only conforming choice. Not a defect.

  8. New keys in every pack. chart.refreshing sits in the top-level chart block and dashboard.refreshing in the top-level dashboard block, each right after that block's loading key: en.ts:1340/:1425, zh.ts:1171/:1225, ar.ts:1152/:1174, de.ts:1132/:1154, ja.ts:1132/:1154, ko.ts:1132/:1154, fr.ts:1134/:1156, es.ts:1136/:1158, pt.ts:1131/:1153, ru.ts:1152/:1174 (block ownership verified by scan for en, zh, ar; the other seven hunks show the identical structure and indent). RIGHT: 「chart and data table get keys in every locale」, 10 of 10. Translation spot-check against each pack's own grid.refreshing: zh 刷新中…, de Wird aktualisiert…, ja 更新中…, ko 새로고침 중…, fr Actualisation…, es Actualizando…, pt Atualizando…, ru Обновление…, ar جارٍ التحديث…, en Refreshing…. Every non-English pack carries its own real translation, equal to its grid.refreshing; none is an English copy. (de list.refreshing reads Aktualisiere…; the new keys follow grid.refreshing, a pre-existing wording split the PR notes and rightly leaves alone.)

  9. Caller census, whole tree at head (git grep RefreshIndicator): five render sites, all named: grid :5684, list :4851, chart :1521, data table :1055, and packages/plugin-gantt/src/ObjectGantt.tsx:2207 which already passed t('gantt.aria.refreshing') and is untouched (no gantt file in the diff). No app, example, content/docs page, README or pre-existing test renders it; the objectstack checkout has zero hits. Nothing asked is missing on this axis, and the ObjectGantt unchanged half of the claim's pin holds.

  10. Anything done that was not asked: nothing. The two added code comments sit inside the claim's file surface (「The RefreshIndicator line, plus its translation hook if needed」). No unrelated line moves.

  11. Existing pins edited or deleted: none. All five test files are additions; no M or D on any test path. No pre-existing test asserted the old bare Refreshing default (grep over *.test.ts(x) at head, excluding the new pins: zero hits). The gantt pin ObjectGantt.refreshInPlace-7237.test.tsx:191,:284-286 asserts its own gantt.aria.refreshing values, which this PR does not touch.

  12. Can the new pins fail? Yes, each has a lit control and a negative leg.

  • packages/components/src/__tests__/refresh-indicator-required-name-10580.test.tsx: TYPE leg :32-40, @ts-expect-error on RefreshIndicator active with no name; it is in the type-check program, since packages/components/tsconfig.test.json includes src/**/*.test.tsx and the package type-check script is tsc --noEmit && tsc -p tsconfig.test.json. If the prop goes optional again the directive is unused (TS2578) and Type Check goes red. DOM legs: :42-46 exact passed name; :53-58 a cast past the type yields NO aria-label attribute at all (fails at the merge-base, where it would read Refreshing).
  • Grid :103-112 (de expects Wird aktualisiert…, negative not.toMatch(/Refreshing/), en control Refreshing…), list :84-93 (zh expects 刷新中…), chart :94-103 (zh), data table :104-113 (ja expects 更新中…): each non-English leg fails at the merge-base (bar named Refreshing), and the en control also discriminates, because the pack value Refreshing… differs from the old literal Refreshing. Each helper first asserts no bar while idle, then holds the second query open so the bar is read genuinely in flight; chart and table stub their renderers so the only progressbar is the indicator.

② Semver level

Changeset .changeset/10580-refresh-indicator-translated-name.md: @object-ui/components: minor; plugin-grid, plugin-list, plugin-charts, plugin-dashboard, i18n: patch. All six packages are in the fixed group of .changeset/config.json, each has a src/ change, and AGENTS.md:168 says every such change owes a .changeset/*.md: satisfied by one file naming all six. AGENTS.md:271 (版本号策略): 「changeset 里不要声明 major … objectui 自身的破坏性变更也标 minor(在正文里写清 breaking 语义即可)」. minor for the breaking components change is exactly that rule; no major is declared. The five patch entries are fix-level changes riding a fix; the fixed group bumps together at the highest level regardless. Not a docs-only change (no content/docs/** path), so no empty-frontmatter form applies. Level: RIGHT.

Changeset prose, sentence by sentence:

  1. 「RefreshIndicator's ariaLabel prop is now required and has no default.」 TRUE (:45, :59).
  2. 「It used to default to the English literal "Refreshing", so the progress bar on every view that passed no name was announced in English … in every locale」 TRUE against the merge-base.
  3. 「A RefreshIndicator rendered without ariaLabel now fails to type-check.」 TRUE (required prop; pinned by the TYPE leg).
  4. 「The bump is minor by this repo's release model: objectui's major follows the @objectstack family major, and its own breaking changes ship as minor」 TRUE (AGENTS.md:268, :271).
  5. 「Migration: pass the bar's accessible name from your own translation layer, for example ariaLabel={t('grid.refreshing')}.」 TRUE and matches the grid site.
  6. 「The component renders the string you pass as the bar's aria-label and does not translate it.」 TRUE (:77).
  7. 「ObjectGrid, ListView, ObjectChart and ObjectDataTable now name their refresh bar in the active locale.」 TRUE (four render lines).
  8. 「The grid and the list read the grid.refreshing and list.refreshing keys their pull-to-refresh text already uses.」 TRUE (merge-base :5690, :4189).
  9. 「The chart reads chart.refreshing and the dashboard data table reads dashboard.refreshing, both with no English literal behind them, so a host that renders either one with no i18next instance at all gets the key itself as the bar's name.」 TRUE (provider.tsx:934-950; the qualifier 「no i18next instance at all」 is the precise condition, since a react-i18next global instance would resolve normally).
  10. 「new keys chart.refreshing and dashboard.refreshing in every built-in locale pack」 TRUE (10 of 10).

Docs/README prose added by the diff: none (no content/docs/** or README path in the file list). The added source comments are checked above (items 2, 5, 6): all true.

③ Boundary flags

  • PR body flags and open questions. Open question (bare t vs tt with an English literal): answered above, item 7; option A is the only choice that honours 「⛔ No English fallback anywhere」, and the changeset states the provider-less consequence plainly. H5 (ariaLabel="" type-checks; no runtime check): correct, a runtime check was not asked and the type is the gate the triage named. Acceptance notes: the ...props spread after aria-label at :79 is unchanged from the merge-base; the de wording split and the en.ts chart-block comment are pre-existing polish, out of this card. The census claims in the body (five sites, no docs snippet, RefreshIndicatorProps has one docblock reader) were each re-measured and hold.
  • CI at head 641ed9f. First read: 42 check-runs, 29 success, 3 skipped, 10 in progress. Re-poll 1 (13:24Z): 43 check-runs, 38 success, 3 skipped (the two coverage rows and dependabot), 1 in progress (Spec Main Shape Gate), 1 queued (Test, the aggregate); all eight Test (shard n/8), Type Check, Lint, Test (dist pins), Doc Snippet Type Check, Doc Component Type Check, the five changeset checks and Inert vi.mock Specifier Check had concluded success. Re-poll 2 (13:27Z, three minutes later): 43 check-runs, all completed, 40 success, 3 skipped, 0 failure, 0 in progress. Zero failures at any read.
  • Draft? Yes: draft: true. Not merged; mergeable_state: behind.
  • Clause-②. The claim declares Clause-②: yes, not no, and that is the correct declaration: the published ariaLabel prop moves from optional-with-default to required, so the accepted prop set of an exported component narrows and the published .d.ts changes. A Clause-②: no would not hold here, and none was claimed.
  • Did main move under the PR's files since the merge-base? Yes, one file: packages/plugin-list/src/ListView.tsx moved on main via dddbc27 (objectui#10627, the finding(plugin-list): the filter candidates read a lookup's display field as display_field || reference_field, spellings FieldSchema refuses, and never read the declared displayField #10545 filter-candidates fix), hunks at :3662-:3745. The PR's only ListView hunk is at :4848, a disjoint region, and git merge-tree of head onto current main dddbc27 produces a clean tree with no conflict. The other 20 paths are untouched on main since 6516320. Main itself moved four commits (from e0f8202 to dddbc27); the PR was last merged with main at 6516320 and is behind but cleanly mergeable.

Implemented-by: claude/issue-10580-refresh-indicator-translated-name
Reviewed-by: session_01KUxVUa7e39aNjhkKi1gsoy

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 25, 2026 13:43
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 25, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3051.3 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-Bp6TikNj.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) 546.98KB 130.82KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 260.06KB 65.86KB
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) 74.99KB 20.92KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.65KB 35.43KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.96KB 61.65KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 152.20KB 39.05KB
plugin-gantt (index.js) 169.64KB 41.91KB
plugin-grid (index.js) 215.41KB 58.93KB
plugin-kanban (index.js) 48.35KB 15.08KB
plugin-list (index.js) 114.63KB 28.32KB
plugin-map (index.js) 22.42KB 7.38KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.67KB 8.95KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.84KB 21.96KB
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

Merge-queue eviction at 13:55Z: not this PR's failure

domain:ui seat 5, session_01KUxVUa7e39aNjhkKi1gsoy, 2026-09-25T14:45Z.

  • Failing check: merge-group CI run 36142794712, job Test (shard 2/8) (108096448798). One test failed there: apps/console/src/pages/developer/ApiConsolePage.requestPreset-10591.test.tsx › "the standalone developer/api-console route honours the preset", at its findByPlaceholderText('/api/v1/...'). The DOM dump is the console boot screen.

  • Why it is not this PR's: that test landed with PR objectui#10632, and it fails intermittently on changes that do not touch it. objectui#10645 records the same failure on PR objectui#10644. This PR's render path renders no RefreshIndicator on that route. The dev measured the file 10 times on each tree through os-verify-lock, and it passed 10 of 10 on both:

    • plain main 4a3d500: the case's median was 325 ms;
    • this branch merged with it (1a89050): the median was 313.5 ms.

    Main's own push CI and PR objectui#10641's merge group both passed on 4a3d500.

  • Fix: none exists yet. objectui#10645 is filed and not yet graded, and the seat adds the dev's root-cause measurement there. ⛔ This PR does not touch that test and does not widen.

  • Next: the branch merged origin/main 4a3d500 as 1a89050, a merge only, with the PR's own diff unchanged. The seat re-runs the at-tier contract review on 1a89050, then re-enqueues it once, which is the one re-run this failure gets.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

CI at 1a89050: the objectui#10645 test failed again, so this PR is blocked on that card's fix

domain:ui seat 5, session_01KUxVUa7e39aNjhkKi1gsoy, 2026-09-25T15:03Z. The at-tier re-review of 1a89050 PASSed; its record follows. CI at this head is red on one check:

  • Test (shard 2/8) (job 108117825154): ApiConsolePage.requestPreset-10591.test.tsx:299, "the standalone developer/api-console route honours the preset". The DOM is the console boot screen: the objectui#10645 signature again.
  • The shard's other 456 files passed. The other seven shards, Type Check, Lint and the doc checks are green.

That is the second failure of this test on this PR: once in the merge group, and once on the branch head. So it is not re-run again. The review judged that this diff cannot plausibly change that route's outcome: the route renders none of the changed components, and the diff adds no module-scope statement to any non-test source. The fix belongs to objectui#10645. The seat posts a proposed patch there, and this PR stays unwidened.

This PR is ready in every other respect (reviewed PASS at tier on this head, Clause-②: yes recorded). It re-enters the merge queue once objectui#10645's fix is on main, through one origin/main merge and a fresh ACCEPT.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 1a89050c264dc1607a1f366df746cef4fb671971

Re-review at the PR's new head. 1a89050 is a merge commit with parents 641ed9fa58802e7419e18f635530356914428f75 (the head the at-tier PASS in PR comment 5833368104 reviewed) and 4a3d500fd94e0ba7c07fff7057fe1837d7764ee5 (origin/main at the time of the merge, objectui#10620). That prior record was read as the baseline; every judgment below was re-derived from the head, not carried over.

Reviewed read-only: card objectui#10580 (body and its five comments: triage 5831898614, claim 5832044126, the two os-dev-reports 5833029249 and 5834284916, read but not used as evidence, and the seat's ACCEPT 5833403617, which appeared in the thread dump and was not used), PR objectui#10635 body, its REST file list and its five PR comments (the prior record 5833368104 and the seat's eviction comment 5834324693), the check-runs at 1a89050 (three reads), objectui#10645 and the evicting merge-group run 36142794712 / job 108096448798 that the PR's seat comment cites, AGENTS.md (版本号策略 at :267-273, the changeset rule at :168, 测试纪律 at :252-266, 多 agent 协作纪律 at :275-348), .changeset/config.json, and the code via refs/review/pr-10635 against merge-base 4a3d500 with refs/review/main at f99f9cde75bc563b18b39a37b867214bc1866fc7. Zero GitHub writes. The diff consumes no @objectstack/spec symbol (no spec import is added), so no spec read was owed.

Diff shape at the new merge-base, measured by git: 21 files, +543 −8, the same 21 paths as the REST file list: 1 changeset added, 1 component source edited, 4 caller sources edited, 10 locale packs edited (+2 each), 5 pin files added. No file is deleted and no existing test file is touched. Compared hunk for hunk with the reviewed diff (6516320..641ed9f), the two are identical except the @@ header of the single ListView.tsx hunk (4848 became 4871), the shift main's objectui#10620 lines above it cause. The merge itself: the tree of 1a89050 (832f77d0e47b085073361e004f43b709fb2359d9) equals git's own clean merge-tree of 641ed9f and 4a3d500, so the merge carries no hand-resolved bytes. packages/plugin-list/src/ListView.tsx at the head differs from 4a3d500 by exactly this PR's one ariaLabel line at :4874, and the file's 641ed9f..1a89050 change equals its 6516320..4a3d500 change line for line, so main's objectui#10620 hunks are carried whole and the PR's line is intact.

① Derived judgments

Binding text, quoted verbatim. Triage 5831898614: 「each caller passes its translated name (grid and list reuse grid.refreshing / list.refreshing; chart and data table get keys in every locale). Removing the English default (making ariaLabel required) is the declaration-enforces option and may ride the same PR with Clause-②: yes.」 Claim 5832044126: 「RefreshIndicator's published ariaLabel prop loses its English default and becomes required」, file surface 「packages/components/src/custom/refresh-indicator.tsx: ariaLabel becomes required, with no English default, and its docblock」, 「The RefreshIndicator line, plus its translation hook if needed, in plugin-grid/src/ObjectGrid.tsx, plugin-list/src/ListView.tsx, plugin-charts/src/ObjectChart.tsx and plugin-dashboard/src/ObjectDataTable.tsx」, 「New chart and data-table refreshing keys in every pack under packages/i18n/src/locales/*.ts」, 「Pins beside each; changesets per touched package」; pin 「each of the four views names its progress bar in the active locale (a non-English control), and ObjectGantt is unchanged」. The card's class: objectui#3546, 「text a user meets that bypasses the translation layer」.

  1. Published surface: ariaLabel goes from optional-with-English-default to required, no default. packages/components/src/custom/refresh-indicator.tsx:45 reads ariaLabel: string (merge-base: ariaLabel?: string under a docblock reading 'Defaults to "Refreshing"'); the destructure at :57-62 has no default (merge-base had ariaLabel = "Refreshing"); :77 still renders aria-label={ariaLabel} on the role="progressbar" element at :75. RIGHT: exactly the triage's declaration-enforces option, taken with Clause-②: yes as the triage allows. The accepted prop set narrows, which is the enforcement asked for, not a weakened guard. It is a published surface: packages/components/src/custom/index.ts:25 re-exports the module (export * from './refresh-indicator') and all five plugins import RefreshIndicator from @object-ui/components. RefreshIndicatorProps readers at the head: the component, the new components pin, a docblock at packages/plugin-dashboard/src/MetricWidget.tsx:212 and a CHANGELOG line; no Omit / Partial / ComponentProps consumer breaks.

  2. Docblock rewrite at refresh-indicator.tsx:38-44. RIGHT, the claim's file surface names 「and its docblock」. Sentences: 「Accessible name of the progress bar, rendered as its aria-label」 true (:77); 「Required, with no default」 true (:45, :59); 「An English default here reached every non-English screen-reader user through the callers that passed nothing」 true against the merge-base, where four of five callers passed nothing.

  3. ObjectGrid passes t('grid.refreshing') at packages/plugin-grid/src/ObjectGrid.tsx:5684. RIGHT: the triage says grid reuses grid.refreshing. t is the one from useGridTranslation() at :1314, the createSafeTranslation(GRID_DEFAULT_TRANSLATIONS, 'grid.actions') hook at :360 whose defaults map already carried 'grid.refreshing': 'Refreshing…' at :324 before this PR, and the pull-to-refresh text at :5690 already reads the same key. No rebinding of t between :1315 and :5684 (grep). No new grid key, as asked.

  4. ListView passes t('list.refreshing') at packages/plugin-list/src/ListView.tsx:4874. RIGHT, same reasoning: t from useListViewTranslation() at :1089 (a createSafeTranslation hook at :941, defaults map entry at :898), the key the pull-to-refresh text at :4212 already reads. The only t mention between the hook and the render line is a dependency array at :2769, not a rebinding. The site moved from :4851 at the reviewed head to :4874 here, by main's objectui#10620 lines above it; the line's content is unchanged.

  5. ObjectChart passes t('chart.refreshing') at packages/plugin-charts/src/ObjectChart.tsx:1521, with t newly destructured at :558 from the useObjectTranslation() call the file already made for language (merge-base :553). RIGHT against the card. The file's other translator, tt = useSafeTranslate() at :544, has the signature (keyOrKeys, fallback: string) and returns the English fallback whenever every key misses (packages/i18n/src/useSafeTranslation.ts:95-106), so naming the bar through tt would put an English literal at the call site, which is the objectui#3546 class the card files under; the bare t keeps the pack as the only source, which is what 「chart and data table get keys in every locale」 asks for. No rebinding of t between :559 and :1521. The added comment at :553-557 is true sentence by sentence: tt does take an English fallback at every call, and the pack is the only source of the name.

  6. ObjectDataTable passes t('dashboard.refreshing') at packages/plugin-dashboard/src/ObjectDataTable.tsx:1055, from the file's existing const { t } = useObjectTranslation() at :623. RIGHT. The added comment at :1053-1054 (「no English default here, unlike noDataSourceLabel above」) is true: noDataSourceLabel at :612 is the English literal 'No data source available for', replaced by the pack value only when the key resolves (:624-625). No rebinding of t between :624 and :1055.

  7. Provider-less consequence, judged. useObjectTranslation (packages/i18n/src/provider.tsx:889) wraps react-i18next's useTranslation; with no i18next instance at all react-i18next hands back notReadyT, which with no defaultValue returns the key, so the chart's bar is then named chart.refreshing and the table's dashboard.refreshing, while grid and list resolve English through their pre-existing createSafeTranslation defaults maps (useSafeTranslation.ts:70-78 returns fallbackT only when t(testKey) === testKey). The diff adds no English fallback: the only ?? in the added lines is inside a test stub (schema.data ?? []), and the literal Refreshing appears in added lines only as the two en pack values, in comments and in test expectations. The asymmetry between the two pairs is inherited from the hooks the triage told the seat to reuse, not introduced here, and the changeset states it (②). Not a defect.

  8. New keys in every pack. chart.refreshing sits in the top-level chart block and dashboard.refreshing in the top-level dashboard block, each directly after that block's loading key: en.ts:1340/:1425, zh.ts:1171/:1225, ar.ts:1152/:1174, de.ts:1132/:1154, ja.ts:1132/:1154, ko.ts:1132/:1154, fr.ts:1134/:1156, es.ts:1136/:1158, pt.ts:1131/:1153, ru.ts:1152/:1174. Block ownership was verified for all ten packs by an indentation walk from each added line to its indent-2 block opener, and each of those two blocks now holds exactly one top-level refreshing key (no duplicate). RIGHT: 「chart and data table get keys in every locale」, 10 of 10. Values, each equal to that pack's own grid.refreshing: en Refreshing…, zh 刷新中…, ar جارٍ التحديث…, de Wird aktualisiert…, ja 更新中…, ko 새로고침 중…, fr Actualisation…, es Actualizando…, pt Atualizando…, ru Обновление…. No non-English pack carries an English copy. (de list.refreshing reads Aktualisiere…; the new keys follow grid.refreshing, a pre-existing wording split the PR notes and rightly leaves alone.)

  9. Caller census, whole tree at the head (git grep RefreshIndicator): five render sites, all named: grid :5684, list :4874, chart :1521, data table :1055, and packages/plugin-gantt/src/ObjectGantt.tsx:2224, which already passed t('gantt.aria.refreshing') and is untouched (no gantt file in the diff). The same five sites exist on main at 4a3d500 and at f99f9cd, so neither merge brought in a new site. No app, example, content/docs page, README or pre-existing test renders it, and the objectstack checkout has zero hits. Nothing asked is missing on this axis, and the 「ObjectGantt is unchanged」 half of the claim's pin holds by the diff.

  10. Anything done that was not asked: nothing. The two added source comments sit inside the claim's file surface; no unrelated line moves; the merge commit's second parent brings only main's own 60 files, none of them re-touched by the PR.

  11. Existing pins edited or deleted: none. All five test paths are added in the REST file list and A in git; no pre-existing test asserted the old bare Refreshing default (grep over *.test.ts(x) at the head, excluding the 10580 pins: zero hits). AGENTS.md's [WIP] Update documentation for project #11 ban on path:line citations: the added lines and the PR body carry none (grep).

  12. Can the new pins fail? Yes, each has a lit control and a negative leg.

  • packages/components/src/__tests__/refresh-indicator-required-name-10580.test.tsx: TYPE leg :32-40, @ts-expect-error on a RefreshIndicator element carrying active only; the file is in the program of packages/components/tsconfig.test.json (include src/**/*.test.tsx), which the package type-check script chains (package.json:32, tsc --noEmit && tsc -p tsconfig.test.json), so an optional prop leaves the directive unused (TS2578) and turns Type Check red. DOM legs: :42-46 the bar is named with exactly the passed string; :53-58 a caller that casts past the type gets no aria-label attribute at all, which fails at the merge-base where the attribute reads Refreshing.
  • grid :103-112 (de expects Wird aktualisiert…, negative not.toMatch(/Refreshing/), en control expects Refreshing…), list :84-93 (zh expects 刷新中…), chart :94-103 (zh), data table :104-113 (ja expects 更新中…): each non-English leg fails at the merge-base, where the bar is named Refreshing, and each en control also discriminates because the pack value Refreshing… differs from the old literal Refreshing (a string name in RTL is an exact match). Each helper first asserts no bar while idle, then holds the second query open with a never-settling Promise so the bar is read genuinely in flight; chart and table stub their renderers so the indicator is the only progressbar. Every rendered module is imported at module scope, so no lazy boundary sits inside a findBy / waitFor window, the shape AGENTS.md 测试纪律 asks for.

② Semver level

Changeset .changeset/10580-refresh-indicator-translated-name.md:1-8: @object-ui/components: minor; plugin-grid, plugin-list, plugin-charts, plugin-dashboard, i18n: patch. All six packages are in the fixed group of .changeset/config.json, each has a src/ change, and no other package is touched, so AGENTS.md:168 (every published-source change owes a .changeset/*.md) is satisfied by the one file naming all six. AGENTS.md:271 (版本号策略): 「changeset 里不要声明 major … objectui 自身的破坏性变更也标 minor(在正文里写清 breaking 语义即可)」: minor for the breaking components change with BREAKING stated in the prose (:10) is exactly that rule, and no major is declared. Not a docs-only change (no content/docs/** path), so the empty-frontmatter form does not apply. The file is byte-identical to the reviewed head (the merge changed no PR file), so it adds nothing about the round. Level: RIGHT.

Changeset prose, sentence by sentence:

  1. 「RefreshIndicator's ariaLabel prop is now required and has no default.」 TRUE (:45, :59).
  2. 「It used to default to the English literal "Refreshing", so the progress bar on every view that passed no name was announced in English to screen-reader users in every locale」 TRUE against the merge-base.
  3. 「A RefreshIndicator rendered without ariaLabel now fails to type-check.」 TRUE (required prop; pinned by the TYPE leg).
  4. 「The bump is minor by this repo's release model: objectui's major follows the @objectstack family major, and its own breaking changes ship as minor with the breaking semantics stated here.」 TRUE (AGENTS.md:268, :271).
  5. 「Migration: pass the bar's accessible name from your own translation layer, for example ariaLabel={t('grid.refreshing')}.」 TRUE, and it matches the grid site.
  6. 「The component renders the string you pass as the bar's aria-label and does not translate it.」 TRUE (:77).
  7. 「ObjectGrid, ListView, ObjectChart and ObjectDataTable now name their refresh bar in the active locale.」 TRUE (the four render lines).
  8. 「The grid and the list read the grid.refreshing and list.refreshing keys their pull-to-refresh text already uses.」 TRUE (:5690, :4212).
  9. 「The chart reads chart.refreshing and the dashboard data table reads dashboard.refreshing, both with no English literal behind them, so a host that renders either one with no i18next instance at all gets the key itself as the bar's name.」 TRUE (item 7; the qualifier 「no i18next instance at all」 is the precise condition, since a react-i18next global instance resolves normally).
  10. 「@object-ui/i18n: new keys chart.refreshing and dashboard.refreshing in every built-in locale pack.」 TRUE (10 of 10).

Docs/README prose added by the diff: none (no content/docs/** or README path in the file list). The added source comments are judged in items 2, 5 and 6: all true.

PR body prose added for the round (the one 「Round 2」 bullet under Gates), sentence by sentence:

  1. 「the branch merged origin/main 4a3d500 as 1a89050」 TRUE (the merge commit's parents are 641ed9f and 4a3d500).
  2. 「(merge only; this PR's own diff is unchanged at 21 files, +543 / −8)」 TRUE (the head tree equals git's clean auto-merge tree; the diff stat and every hunk are identical to the reviewed diff apart from one @@ header).
  3. 「There, the plugin-list suite plus the five pins and the ObjectGantt 7237 pin (104 files, 1166 tests), and the seven type-checks, re-ran green.」 The file count reconciles with the tree: 99 test files under packages/plugin-list at the head (the list pin among them), plus the four other pins and the gantt pin, is 104. The run itself and the test count are the developer's local readings that cannot be re-derived from the diff; the independent evidence for green at this head is CI (③).
  4. 「The merge-queue eviction at 13:55Z was the objectui#10645 test, which is not this PR's」 TRUE: merge-group run 36142794712 (branch gh-readonly-queue/main/pr-10635-4a3d500…, head 6b6fd885c) concluded failure at 13:55:32Z on job 108096448798 Test (shard 2/8); objectui#10645 names that run and the identical failure on PR objectui#10644 (run 36143553512, five app-shell files, none of this PR's), and main's own push CI at 4a3d500 passed Test (shard 2/8) (job 108094589965). Whether this PR can affect that test is judged in ③.
  5. 「(os-dev-report round 2, comment 5834284916)」 TRUE: that comment exists on the card and is the round-2 report.

Older body sentences that name b240863 and 641ed9f are labelled as readings at those heads and stay true as such; the census sentence 「The merge of origin/main brought in no new render site」 holds for both merges (item 9). The acceptance note 「The console mounts I18nProvider in apps/console/src/main.tsx」 holds at the head.

③ Boundary flags

  • PR body flags and open questions. The open question (bare t against tt with an English literal at the chart and table sites): answered in items 5 and 7; tt would put an English literal at the caller, the card's own class, so option A as shipped is the reading consistent with the card, and the changeset states the provider-less consequence plainly. H5 (ariaLabel="" type-checks, no runtime check): correct; no runtime check was asked, and the type is the gate the triage named. Acceptance notes: the ...props spread after aria-label at :79 is unchanged from the merge-base; the de wording split and the en.ts chart-block comment are pre-existing polish outside this card. The census claims in the body (five sites, no docs snippet, RefreshIndicatorProps has one docblock reader) were each re-measured at this head and hold. The head's Console Performance Budget comment (5834323785) reads green.
  • CI at head 1a89050. First read (14:48Z): 42 check-runs, 29 success, 3 skipped (Test (coverage), Test (coverage shard …/4), dependabot), 10 in progress. Re-poll 1 (14:52Z): 42 check-runs, 32 success, 3 skipped, 7 in progress (Test (shard 2/8), 3, 4, 6, 7, Type Check, Spec Main Shape Gate). Re-poll 2 (14:58Z): 43 check-runs, all completed: 38 success, 3 skipped, 2 failure. The two failures are Test (shard 2/8) (job 108117825154, 14:41:39Z to 14:52:32Z) and the aggregate Test that rolls it up. The shard's log reads Failed Tests 1, Test Files 1 failed | 456 passed (457), Duration 617.59s (transform 73.08s, setup 213.32s, import 1214.37s, tests 229.34s, environment 117.35s); the one failure is apps/console/src/pages/developer/ApiConsolePage.requestPreset-10591.test.tsx at :299, 「the standalone developer/api-console route honours the preset」, TestingLibraryElementError: Unable to find an element with the placeholder text of: /api/v1/..., with the console boot screen (「Initializing application…」, 「Connecting to data source」) as the DOM dump: the objectui#10645 signature, the same case that evicted the PR from the queue at 13:55Z. The shard's check-run carries exactly one test annotation (plus the process-exit annotation and a runner notice), so no other test in that shard failed, and none of this PR's five pins failed in any shard. Type Check, Lint, Test (dist pins), Doc Snippet Type Check, Doc Component Type Check, Spec Main Shape Gate, Governed Surface Queue Guard and the other seven test shards are green, which is also the corroboration for the Round 2 sentence's type-checks and pins. The green at the reviewed head 641ed9f was no control for this test: the file landed on main in eca2760 (objectui#10632), after 6516320, so it was absent from the PR's tree until this merge; the controls are main's own push CI at 4a3d500 (shard 2 success, job 108094589965) and the passing queue runs objectui#10645 lists, against its failures on PR objectui#10644 and in this PR's queue run. This red is a flag for the seat's re-enqueue, not a defect of the diff: the test, its failure signature and its base rate are objectui#10645's (judged below).
  • Draft? No: draft: false (the seat converted it to ready before the enqueue; auto_merge is null after the eviction; mergeable: true, mergeable_state: behind).
  • Clause-②. The claim declares Clause-②: yes, and at this head that is the correct declaration: the published ariaLabel prop moves from optional-with-default to required (:45, :59), so the accepted prop set of an exported component narrows and the published .d.ts changes. A Clause-②: no would not hold here, and none was claimed.
  • Did main move under the PR's files since the merge-base? No. Main is at f99f9cd, one commit past 4a3d500 (objectui#10641 for objectui#10565): six files, all under packages/plugin-form, content/docs/plugins/plugin-form.mdx and two changesets, with zero overlap with the PR's 21 paths, and git merge-tree of the head onto f99f9cd is clean.
  • The eviction: can this diff plausibly affect the developer/api-console route? No. The failing case (apps/console/src/pages/developer/ApiConsolePage.requestPreset-10591.test.tsx:293-301) renders app-shell's DefaultAppContent with the console's real systemRoutes, where the route is a lazy ApiConsolePage under Suspense with a LoadingScreen fallback (apps/console/src/AppContent.tsx:25, :218), and awaits findByPlaceholderText in the default window. ApiConsolePage.tsx imports only @object-ui/components primitives, useAdapter, lucide and its own hook (:30-43). No file under apps/console/src/pages/developer/ or packages/app-shell mentions RefreshIndicator, ObjectGrid, ListView, ObjectChart or ObjectDataTable as a render (the five sites in item 9 are the whole census), so the route renders none of the changed components. The edited plugin sources can be in that test's module graph only through app-shell's static imports of ListView (views/ObjectView.tsx:26, views/ObjectDataPage.tsx:31, views/InterfaceListPage.tsx:20) and DashboardRenderer (views/DashboardView.tsx:14); but the diff adds no module-scope statement to any non-test source (all 41 added non-test source lines are inside component bodies, the props interface, a docblock, or pack leaf keys; every added import is in one of the five pin files), so module evaluation on that path is unchanged, and the test's own partial mock of @object-ui/i18n replaces useObjectTranslation on that route anyway. The only channels left are vitest's shard composition (five new test files re-hash the shard split) and machine load, which is the unbounded-module-load-inside-a-bounded-window class AGENTS.md 测试纪律 describes: not this PR's code, seen on PR objectui#10644 which touches none of these files, and absent on main's own shard 2 at 4a3d500. The head's own CI red (above) is the same case with the same DOM signature under the same saturation (import 1214s aggregate inside a 618s shard), and it does not change this reading: the seat comment's claim holds; the test is not this PR's, and this PR cannot plausibly change its outcome beyond the load it shares with every other change in that shard.

Implemented-by: claude/issue-10580-refresh-indicator-translated-name
Reviewed-by: session_01KUxVUa7e39aNjhkKi1gsoy

VERDICT: PASS

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3055.9 KB 3104.5 KB
Main entry chunk (gzip) 147.9 KB 350 KB
Entry file index-DZJ2gqZi.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.20KB 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.85KB 21.36KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.65KB 35.43KB
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.70KB 59.29KB
plugin-kanban (index.js) 48.35KB 15.08KB
plugin-list (index.js) 115.82KB 28.76KB
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: a6512a771ba0ccc075659c0e94b13888fbc51a7d

Re-review at the PR's new head. a6512a7 is a merge commit with parents 1a89050c264dc1607a1f366df746cef4fb671971 (the head the previous at-tier PASS in PR comment 5834625522 reviewed) and 7baede3745904d929d4afd80bcd5e2314b466072 (origin/main at the time of the merge; its subject is 「test(console): wait for the lazy route to settle in the API console route-table cases (objectui#10645) (#10677)」, so it is PR objectui#10677's commit). That prior record was read as the baseline; every judgment below was re-derived from this head, not carried over.

Reviewed read-only: card objectui#10580 (body and its six comments: triage 5831898614, claim 5832044126, the three os-dev-reports 5833029249, 5834284916 and 5836351479, read but not used as evidence, and the seat's ACCEPT 5833403617, which appeared in the thread dump and was not used), PR objectui#10635 body and REST file list, the prior record 5834625522, the check-runs at a6512a7 (three reads), AGENTS.md (#11 at :107, the changeset rule at :168, 测试纪律 at :252-266, 版本号策略 at :267-273), .changeset/config.json, and the code via refs/review/pr-10635 against merge-base 7baede3 with refs/review/main also at 7baede3. Zero GitHub writes. The diff consumes no @objectstack/spec symbol (every added import is in one of the five pin files and names vitest, react, testing-library, @object-ui/react, @object-ui/i18n, @object-ui/fields or a sibling source), so no spec read was owed.

The merge, measured by git. The tree of a6512a7 (b9a332d077ae818b56da7d3967c5e3ab34db6760) equals git merge-tree --write-tree of 1a89050 and 7baede3 (exit 0, no conflict output), so the merge carries no hand-resolved bytes. Main's 4a3d500..7baede3 is 17 commits over 95 files; its overlap with the PR's 21 paths is exactly packages/plugin-charts/src/ObjectChart.tsx, packages/plugin-grid/src/ObjectGrid.tsx and packages/plugin-list/src/ListView.tsx. For each of those three, the file's 1a89050..a6512a7 change equals its 4a3d500..7baede3 change hunk for hunk (96, 180 and 243 patch lines respectively; ObjectChart's three main hunks from objectui#10634 and objectui#10587, ObjectGrid's six from objectui#10607 and the bus work, ListView's thirteen from objectui#10623 and objectui#10607), so main's hunks are carried whole, and each file's 7baede3..a6512a7 diff holds only this PR's own hunk(s): ObjectChart @@ -553 and @@ -1574, ObjectGrid @@ -5805, ListView @@ -4980. The RefreshIndicator lines are intact next to main's changes: ObjectChart.tsx:1582, ObjectGrid.tsx:5808, ListView.tsx:4983.

Diff shape at the new merge-base: 21 files, +543 −8 by both git and REST, the same 21 paths: 1 changeset added, 1 component source edited, 4 caller sources edited, 10 locale packs edited (+2 each), 5 pin files added. No file deleted, no existing test touched. Compared line for line with the previously reviewed diff (4a3d500..1a89050), the two are identical except the @@ headers of four hunks in the three auto-merged files (ObjectChart 550 to 553 and 1513 to 1574, ObjectGrid 5681 to 5805, ListView 4871 to 4980, the shift main's lines above them cause) and the blob ids of those three files. Every added and removed line is the same.

① Derived judgments

Binding text, quoted verbatim. Triage 5831898614: 「each caller passes its translated name (grid and list reuse grid.refreshing / list.refreshing; chart and data table get keys in every locale). Removing the English default (making ariaLabel required) is the declaration-enforces option and may ride the same PR with Clause-②: yes.」 Claim 5832044126: 「RefreshIndicator's published ariaLabel prop loses its English default and becomes required」; file surface 「packages/components/src/custom/refresh-indicator.tsx: ariaLabel becomes required, with no English default, and its docblock」, 「The RefreshIndicator line, plus its translation hook if needed, in plugin-grid/src/ObjectGrid.tsx, plugin-list/src/ListView.tsx, plugin-charts/src/ObjectChart.tsx and plugin-dashboard/src/ObjectDataTable.tsx」, 「New chart and data-table refreshing keys in every pack under packages/i18n/src/locales/*.ts」, 「Pins beside each; changesets per touched package」; pin 「each of the four views names its progress bar in the active locale (a non-English control), and ObjectGantt is unchanged」. The card's class: objectui#3546, 「text a user meets that bypasses the translation layer」.

  1. Published surface: ariaLabel goes from optional-with-English-default to required, no default. packages/components/src/custom/refresh-indicator.tsx:45 reads ariaLabel: string (merge-base: ariaLabel?: string under 'Accessible label. Defaults to "Refreshing".'); the destructure at :57-62 has no default (merge-base had ariaLabel = "Refreshing"); :75 keeps role="progressbar" and :77 still renders aria-label={ariaLabel}. RIGHT: exactly the triage's declaration-enforces option, taken with Clause-②: yes as the triage allows. The accepted prop set narrows, which is the enforcement asked for, not a weakened guard. It is a published surface: packages/components/src/custom/index.ts:25 re-exports the module (export * from './refresh-indicator') and the four plugins import RefreshIndicator from @object-ui/components (ObjectChart :7, ObjectDataTable :19, ListView :10, ObjectGrid :54). RefreshIndicatorProps readers at the head: the component (:35, :57), the new components pin (:29, :54), a docblock at packages/plugin-dashboard/src/MetricWidget.tsx:212 and a line in packages/plugin-dashboard/CHANGELOG.md:566; no Omit / Partial / ComponentProps consumer breaks.

  2. Docblock rewrite at refresh-indicator.tsx:38-44. RIGHT: the claim's file surface names 「and its docblock」. Sentences: 「Accessible name of the progress bar, rendered as its aria-label」 true (:77); 「Required, with no default」 true (:45, :59); 「An English default here reached every non-English screen-reader user through the callers that passed nothing」 true against the merge-base, where four of five callers passed nothing (census at 7baede3: chart :1577, table :1053, grid :5808, list :4983 all bare; gantt :2251 named).

  3. ObjectGrid passes t('grid.refreshing') at packages/plugin-grid/src/ObjectGrid.tsx:5808. RIGHT: the triage says grid reuses grid.refreshing. t is from const { t } = useGridTranslation() at :1424, the createSafeTranslation(GRID_DEFAULT_TRANSLATIONS, 'grid.actions') hook at :470 whose defaults map already carried 'grid.refreshing': 'Refreshing…' at :434 before this PR; the pull-to-refresh text at :5814 reads the same key. No rebinding of t between :1424 and :5808 (regex over the range: zero hits). No new grid key, as asked. Main's objectui#10607 hunks in this file sit at :25-:2477 and do not touch the hook, the defaults map or the render line.

  4. ListView passes t('list.refreshing') at packages/plugin-list/src/ListView.tsx:4983. RIGHT, same reasoning: const { t } = useListViewTranslation() at :1190 (a createSafeTranslation hook at :1042, defaults entry 'list.refreshing': 'Refreshing…' at :999), the key the pull-to-refresh text at :4321 already reads. No rebinding of t between :1190 and :4983. The site moved from :4874 at the previous head to :4983 here by main's thirteen hunks above it; the line's content is unchanged.

  5. ObjectChart passes t('chart.refreshing') at packages/plugin-charts/src/ObjectChart.tsx:1582, with t newly destructured at :561 from the useObjectTranslation() call the file already made for language (merge-base :556 reads const { language } = useObjectTranslation()). RIGHT against the card. The file's other translator, tt = useSafeTranslate() at :547, takes (keyOrKeys: string | string[], fallback: string) and returns a string (packages/i18n/src/useSafeTranslation.ts:95), handing back the English fallback when every key misses (:106), so naming the bar through tt would put an English literal at the call site, which is the card's own class; the bare t keeps the pack as the only source, which is what 「chart and data table get keys in every locale」 asks for. No rebinding of t between :561 and :1582. The added comment at :557-560 is true sentence by sentence. Main's objectui#10634 / objectui#10587 hunks in this file (@@ -153, @@ -1181, @@ -1274) do not touch :561 or :1582.

  6. ObjectDataTable passes t('dashboard.refreshing') at packages/plugin-dashboard/src/ObjectDataTable.tsx:1055, from the file's existing const { t } = useObjectTranslation() at :623. RIGHT. The added comment at :1053-1054 (「no English default here, unlike noDataSourceLabel above」) is true: noDataSourceLabel at :612 is the English literal 'No data source available for', replaced by the pack value only when the key resolves (:624-625). No rebinding of t between :623 and :1055. Main did not touch this file.

  7. Provider-less consequence, judged. useObjectTranslation (packages/i18n/src/provider.tsx:889) wraps react-i18next's useTranslation (:893); with no i18next instance at all react-i18next returns the key, so the chart's bar is then named chart.refreshing and the table's dashboard.refreshing, while grid and list resolve English through their pre-existing createSafeTranslation defaults maps (useSafeTranslation.ts:70-77 returns fallbackT only when t(testKey) === testKey). The diff adds no English fallback: the only ?? in the added lines is inside a test stub (schema.data ?? []), and the literal Refreshing appears in added lines only as the two en pack values, in comments and in test expectations. The asymmetry is inherited from the hooks the triage told the seat to reuse, not introduced here, and the changeset states it (②). Not a defect.

  8. New keys in every pack. chart.refreshing sits in the top-level chart block and dashboard.refreshing in the top-level dashboard block, each directly after that block's loading key: en.ts:1340/:1425 (blocks open at :1338/:1404), zh.ts:1171/:1225, ar.ts:1152/:1174, de.ts:1132/:1154, ja.ts:1132/:1154, ko.ts:1132/:1154, fr.ts:1134/:1156, es.ts:1136/:1158, pt.ts:1131/:1153, ru.ts:1152/:1174. Block ownership verified for all ten packs by an indentation walk from each added line to its indent-2 opener (chart: { / dashboard: {), and each of those two blocks holds exactly one indent-4 refreshing key (no duplicate). RIGHT: 「chart and data table get keys in every locale」, 10 of 10. Values, each equal to that pack's own grid.refreshing: en Refreshing…, zh 刷新中…, ar جارٍ التحديث…, de Wird aktualisiert…, ja 更新中…, ko 새로고침 중…, fr Actualisation…, es Actualizando…, pt Atualizando…, ru Обновление…. No non-English pack carries an English copy. (de list.refreshing reads Aktualisiere…; the new keys follow grid.refreshing, a pre-existing wording split the PR notes and rightly leaves alone.) Main's 4a3d500..7baede3 range touches no locale pack, so the pack hunks are unchanged.

  9. Caller census, whole tree at the head (git grep RefreshIndicator): five render sites, all named: grid :5808, list :4983, chart :1582, data table :1055, and packages/plugin-gantt/src/ObjectGantt.tsx:2251, which already passed t('gantt.aria.refreshing') and is untouched (git diff 7baede3..a6512a7 -- packages/plugin-gantt is empty). The same five sites, in the same five files, exist on main at 7baede3 and at 4a3d500; git log -S RefreshIndicator 4a3d500..7baede3 finds zero commits, so no new RefreshIndicator render site arrived from main, and the required prop introduces no type error in main's code. The one main-arrived test that reads the refresh-indicator testid, ObjectGantt.silentOvertake-10633.test.tsx (objectui#10633, main commit 9d89c6f), renders ObjectGantt, which names its bar itself. The independent corroboration is the head's Type Check check-run, green (③). No app, example, content/docs page, README or pre-existing test renders it, and the objectstack checkout has zero hits. Nothing asked is missing on this axis, and the 「ObjectGantt is unchanged」 half of the claim's pin holds by the diff.

  10. Anything done that was not asked: nothing. The two added source comments sit inside the claim's file surface; no unrelated line moves; the merge commit's second parent brings only main's own 95 files, none of them re-touched by the PR beyond git's clean auto-merge.

  11. Existing pins edited or deleted: none. All five test paths are added in the REST file list and A in git. No pre-existing test asserts the old bare Refreshing name (grep over *.test.ts(x) at the head, excluding the 10580 pins, for name: 'Refreshing', aria-label="Refreshing" and the like: zero hits). The four gantt tests that read the refresh-indicator testid assert the name only through gantt's own ariaLabel (ObjectGantt.refreshInPlace-7237.test.tsx:191 expects Refreshing… from GANTT_DEFAULT_TRANSLATIONS, :284 expects 刷新中…), so they are independent of the removed default. AGENTS.md [WIP] Update documentation for project #11 (:107) path:line ban: the added lines and the PR body carry none (regex over every + line and the body: zero hits; the body holds zero angle-bracket characters).

  12. Can the new pins fail? Yes; each has a lit control and a negative leg.

  • packages/components/src/__tests__/refresh-indicator-required-name-10580.test.tsx: TYPE leg :32-40, @ts-expect-error on a RefreshIndicator element carrying active only (:36-37); the file is in the program of packages/components/tsconfig.test.json (include src/**/*.test.tsx), which the package type-check script chains (package.json:32, tsc --noEmit && tsc -p tsconfig.test.json), so an optional prop leaves the directive unused (TS2578) and turns Type Check red. DOM legs: :42-46 the bar is named with exactly the passed string; :48-51 nothing renders while inactive; :53-58 a caller that casts past the type gets no aria-label attribute at all, which fails at the merge-base where the attribute reads Refreshing.
  • grid :103-112 (de expects Wird aktualisiert…, negative not.toMatch(/Refreshing/), en control expects Refreshing…), list :84-93 (zh expects 刷新中…), chart :94-103 (zh), data table :104-113 (ja expects 更新中…): each non-English leg fails at the merge-base, where the bar is named Refreshing, and each en control also discriminates because the pack value Refreshing… differs from the old literal Refreshing (a string name in RTL is an exact match). Each helper first asserts no bar while idle, then holds the second query open with a never-settling Promise so the bar is read genuinely in flight; chart and table stub their renderers so the indicator is the only progressbar. Every rendered module is imported at module scope, so no lazy boundary sits inside a findBy / waitFor window, the shape AGENTS.md 测试纪律 asks for. The chart pin sits at packages/plugin-charts/src/ rather than __tests__/, beside the pre-existing ObjectChart.invalidationRefetch-10035.test.tsx it cites, and the root vitest include packages/**/*.test.tsx (vitest.config.mts:412) covers it; the list pin's refresh-button testid exists at ListView.tsx:4806.

② Semver level

Changeset .changeset/10580-refresh-indicator-translated-name.md:1-8: @object-ui/components: minor; plugin-grid, plugin-list, plugin-charts, plugin-dashboard, i18n: patch. All six packages are in the single fixed group (40 members) of .changeset/config.json, each has a src/ change, and no other package is touched, so AGENTS.md:168 (every published-source change owes a .changeset/*.md) is satisfied by the one file naming all six. AGENTS.md:271 (版本号策略): 「changeset 里不要声明 major … objectui 自身的破坏性变更也标 minor(在正文里写清 breaking 语义即可)」: minor for the breaking components change with BREAKING stated in the prose (:10) is exactly that rule, and no major is declared. Not a docs-only change (no content/docs/** path), so the empty-frontmatter form does not apply. The file is byte-identical to the previously reviewed head (same blob b3f5a06 in both diffs; the merge changed no PR file). Level: RIGHT.

Changeset prose, sentence by sentence:

  1. 「RefreshIndicator's ariaLabel prop is now required and has no default.」 TRUE (:45, :59).
  2. 「It used to default to the English literal "Refreshing", so the progress bar on every view that passed no name was announced in English to screen-reader users in every locale」 TRUE against the merge-base.
  3. 「A RefreshIndicator rendered without ariaLabel now fails to type-check.」 TRUE (required prop; pinned by the TYPE leg).
  4. 「The bump is minor by this repo's release model: objectui's major follows the @objectstack family major, and its own breaking changes ship as minor with the breaking semantics stated here.」 TRUE (AGENTS.md:268, :271).
  5. 「Migration: pass the bar's accessible name from your own translation layer, for example ariaLabel={t('grid.refreshing')}.」 TRUE, and it matches the grid site.
  6. 「The component renders the string you pass as the bar's aria-label and does not translate it.」 TRUE (:77).
  7. 「ObjectGrid, ListView, ObjectChart and ObjectDataTable now name their refresh bar in the active locale.」 TRUE (the four render lines).
  8. 「The grid and the list read the grid.refreshing and list.refreshing keys their pull-to-refresh text already uses.」 TRUE (:5814, :4321).
  9. 「The chart reads chart.refreshing and the dashboard data table reads dashboard.refreshing, both with no English literal behind them, so a host that renders either one with no i18next instance at all gets the key itself as the bar's name.」 TRUE (item 7).
  10. 「@object-ui/i18n: new keys chart.refreshing and dashboard.refreshing in every built-in locale pack.」 TRUE (10 of 10).

Docs/README prose added by the diff: none (no content/docs/** or README path in the file list). The added source comments are judged in items 2, 5 and 6: all true.

PR body prose added for this round (the one 「Round 3」 bullet under Gates), sentence by sentence:

  1. 「merged origin/main 7baede3 (it carries PR objectui#10677, the change for objectui#10645) at a6512a7」 TRUE: the merge commit's parents are 1a89050 and 7baede3; 7baede3's subject names #10677 and objectui#10645, and the 4a3d500..7baede3 range changes apps/console/src/pages/developer/ApiConsolePage.requestPreset-10591.test.tsx (+44 −1), the file the previous round's red was in.
  2. 「with no conflicts」 TRUE: git's own merge-tree --write-tree of the two parents exits 0 and yields the head's tree.
  3. 「the five pins, the ObjectGantt 7237 pin and the seven type-checks re-ran green there」: the developer's local readings, not re-derivable from the diff; the independent evidence at this head is CI (③): Type Check green, and the test shards as reported there.
  4. 「(os-dev-report round 3, comment 5836351479)」 TRUE: that comment exists on the card and is the round-3 report.

Older body sentences that name b240863, 641ed9f and 1a89050 are labelled as readings at those heads and stay true as such; the census sentence 「The merge of origin/main brought in no new render site」 holds for all three merges (item 9). The acceptance note 「The console mounts I18nProvider in apps/console/src/main.tsx」 holds at the head (:158).

③ Boundary flags

  • PR body flags and open questions. The open question (bare t against tt with an English literal at the chart and table sites): answered in items 5 and 7; tt would put an English literal at the caller, the card's own class, so option A as shipped is the reading consistent with the card, and the changeset states the provider-less consequence plainly. H5 (ariaLabel="" type-checks, no runtime check): correct; no runtime check was asked, and the type is the gate the triage named. Acceptance notes: the ...props spread after aria-label at :79 is unchanged from the merge-base; the de wording split and the en.ts chart-block comment are pre-existing polish outside this card. The census claims in the body (five sites, no docs snippet, RefreshIndicatorProps has one docblock reader) were each re-measured at this head and hold.
  • CI at head a6512a7. Three reads, the cap the brief sets. Read 1 (17:10:41Z): 42 check-runs, 26 success, 3 skipped (Test (coverage), Test (coverage shard …/4), dependabot), 13 in progress. Read 2 (17:15:53Z): 42 check-runs, 32 success, 3 skipped, 7 in progress (Test (shard 1/8), 2, 3, 4, 6, 7, 8); by then Type Check (17:13:28Z), Lint (17:12:34Z), Doc Snippet Type Check, Spec Main Shape Gate, Skill Example Check and Test (shard 5/8) had completed green. Read 3 (17:18:30Z): 42 check-runs, 38 success, 3 skipped, 0 failure, 1 in progress: Test (shard 4/8) (check-run 108170597667, started 17:06:42Z, 11m48s in at the read; the other seven shards concluded between 6m41s and 10m52s). No check-run has failed at this head, so there is no failing check and no failing test to name. Test (shard 2/8), the shard that carried the objectui#10645 failure at 1a89050, is success at 17:17:23Z on this head, which holds PR objectui#10677's change to that test. Also green: Type Check (the corroboration that no nameless RefreshIndicator site exists anywhere in the merged tree, that main's code compiles against the required prop, and that the TYPE leg's @ts-expect-error is used), Test (dist pins), Lint, Doc Snippet Type Check, Doc Component Type Check, Spec Main Shape Gate, Governed Surface Queue Guard, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Declaration, Line Citation Gate and Control Byte Scan. The aggregate Test rollup (which appeared as a 43rd run at the previous head once every shard concluded) had not yet appeared. CI is therefore not fully converged at the last read: one shard open, none red. That is a flag for the seat to read shard 4's conclusion before any enqueue, not a defect of the diff.
  • Draft? No: draft: false; mergeable: true, mergeable_state: blocked (checks were still running at the reads), auto_merge: null.
  • Clause-②. The claim declares Clause-②: yes, and at this head that is the correct declaration: the published ariaLabel prop moves from optional-with-default to required (:45, :59), so the accepted prop set of an exported component narrows and the published .d.ts changes. A Clause-②: no would not hold here, and none was claimed.
  • Did main move under the PR's files since the merge-base? No. refs/review/main is 7baede3, which is the merge-base itself, so main has not moved at all since the merge; git merge-tree of the head onto it is the head's own tree.
  • The previous round's red (objectui#10645). The test that was red at 1a89050 and evicted the PR from the queue, ApiConsolePage.requestPreset-10591.test.tsx, is the file PR objectui#10677 changed, and that change is in this head through 7baede3. At this head Test (shard 2/8) is green (17:17:23Z), so the previous round's red does not recur; nothing in this PR's diff touches that route (no file under apps/console or packages/app-shell is in the 21 paths).

Implemented-by: claude/issue-10580-refresh-indicator-translated-name
Reviewed-by: session_01KUxVUa7e39aNjhkKi1gsoy

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit 9fbbb17 Sep 25, 2026
44 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10580-refresh-indicator-translated-name branch September 25, 2026 17:35
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.

finding(components,a11y,i18n): RefreshIndicator defaults its accessible name to the English literal "Refreshing", and four renderers pass none

2 participants