Skip to content

fix(types,layout,app-shell): a nav entry's inline locale-map label passes the door and renders in the viewer's locale; three inert nav resolver props retire (objectui#11299) - #11325

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-11299-nav-label-locale-map
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-11299-nav-label-locale-map

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #11299
Clause-②: yes

A navigation entry's label written as an inline locale map (the spec's I18nLabel, for example { en: 'Accounts', 'zh-CN': '客户' }) is now accepted by objectui validate as the spec accepts it, and it renders the viewer's entry. The three nav resolver props that objectui#11201 left inert are removed.

This follows triage's answers on the card (comment 5922552691): Q3 first, then Q1 A, with Q2 on the same call sites.

What changed

Q3: the authoring door (@object-ui/types).

  • NavigationItemObject.label takes the spec's I18nLabelSchema by reference (stripImportedDefaults(SpecI18nLabelSchema), the same crossing base.zod.ts uses). It was z.string().
  • NavigationEntryItem.label is now the spec's I18nLabel type.
  • The doc arm reads its base keys off the flat object, so it takes the same label.
  • The empty-string refusal (refuseEmptyNavLabel) is unchanged. It is still the one stated divergence from the spec.

Q1 A: the locale (@object-ui/layout plus five console mounts).

  • NavigationRenderer gains an optional locale prop. resolveNavItemLabel gains a trailing locale argument.
  • presentNavItemLabel hands that locale to the spec's own resolveI18nLabel. There is no copy of its fallback order.
  • The console passes useObjectTranslation().language from five mounts: UnifiedSidebar (both the app and home mounts), nav-menu-renderer, CommandPalette and SearchResultsPage.

Q2: the retirement.

  • resolveObjectLabel, resolveDashboardLabel and resolveViewLabel leave NavigationRendererProps.
  • resolveNavItemLabel loses its 2nd, 4th and 5th arguments. Its signature is now (item, t, targetLabel, locale).
  • The callers no longer build those resolvers: UnifiedSidebar keeps only appLabel from useObjectLabel, and nav-menu-renderer drops useObjectLabel entirely.
  • nav-menu-renderer.tsx's header comment no longer promises "convention-based object / view / dashboard i18n resolution". It now describes the rule the sidebar actually applies.

Zone 2 measurements

  1. Q3 red reproduced before the fix. The new pin packages/types/src/__tests__/nav-label-locale-map-11299.test.ts ran against the unfixed mirror. It reported Tests 7 failed | 8 passed (15), with invalid_type at label, and the spec's NavigationItemSchema.safeParse accepted the same entry. The 8 rows that passed are the string control and the refusal rows. After the fix the file reports Tests 15 passed (15). The spec (17.5.0) exports I18nLabelSchema and I18nLabel from @objectstack/spec/ui.
  2. Where the language comes from. UnifiedSidebar resolves area labels as resolveInlineI18nLabel(area.label, language), with language from useObjectTranslation(). It does not call resolveAreaLabel, which belongs to AppSchemaRenderer and has no locale. useNavTargetLabel reads the same useObjectTranslation().language. That value is the locale passed at all five mounts.
  3. Callers (git grep). The card counted "three in-tree mounts and two tests". The measurement found more:
    • Six-argument resolveNavItemLabel calls: CommandPalette, SearchResultsPage, nav-menu-renderer, app-shell views/metadata-admin/previews/navItemLabel.ts (not named on the card), and packages/plugin-designer/src/AppCreationWizard.tsx (not named on the card, and a fourth released package). Tests: three layout files and app-shell navItemLabel.inherit-11196.test.ts.
    • Props: the two UnifiedSidebar mounts. Tests: app-shell useNavigationSync.standardEntryLocalizes-11201, StudioDesignSurface.standardNavEntries-11201, and layout labelVerbatim-11201 and navLabelOwnership.
    • packages/plugin-list/src/ListView.tsx's resolveObjectLabel is unrelated. It is a local alias of useListFieldLabel().objectLabel, used for export naming. It is not the nav prop and was not touched.
    • One more consumer, found by the build. The Q3 type widening surfaced packages/plugin-designer/src/NavigationDesigner.tsx: four reads passed item.label to the keyed-label reader (TS2345). It now goes through a small labelDraftOf helper that keeps the runtime behaviour: a string and a keyed reference read as before, and a map starts the draft empty, which is what the keyed read answered for a map.
  4. AppSchemaRenderer: not taken. Taking a locale would add a public prop to AppSchemaRendererProps. Nothing in this repository mounts that component with a locale to give it, so it fails the "no extra surface" condition. Only its resolveAreaLabel docblock is edited, so that it stays true: it no longer says that a nav entry label is the keyed form, and it states that the component's entries read the same en floor.
  5. The header comment is true, as described above.

Tests

The final union ran at 81579bca12 (git rev-parse --short HEAD):
pnpm exec vitest run --maxWorkers=2 packages/types/ packages/layout/ packages/plugin-designer/ packages/app-shell/src/layout/ plus the 74 app-shell test files that git grep -l finds for NavigationRenderer, resolveNavItemLabel, resolveObjectLabel, CommandPalette, SearchResultsPage or nav-menu-renderer. Result: Test Files 443 passed (443) and Tests 8875 passed (8875), VERDICT command-exit 0.

New pins:

  • nav-label-locale-map-11299.test.ts (types). The map passes safeValidateSchema and the strict face exactly as the spec's safeParse does, with a string control. A parity table checks that the mirror's verdict equals the spec's for nine label shapes (keyed reference, non-string entry and non-locale key refused by both).
  • NavigationRenderer.labelLocaleMap-11299.test.tsx (layout).
    • zh-CN reads 客户, and no locale reads the en default.
    • A parity table checks that resolveNavItemLabel equals the spec's resolveI18nLabel for twelve (map, locale) pairs, which pins "no copy of the fallback order".
    • The sidebar renders the group heading, the entry, the action and Favorites, and search finds the entry, all in the given locale.
    • The retired props and the six-argument call are @ts-expect-error lines. The layout type-check compiles them, so a re-added prop or argument fails that check.
  • App-shell rows for every mount (sidebar app mount, nav:menu, palette, search). Each renders a map-labelled entry under zh-CN and under en, through a real I18nProvider; the sidebar harness uses its mocked language instead.

Ablation:

  • presentNavItemLabel was changed to drop the locale (the locale argument of its resolveInlineI18nLabel call became undefined), applied with ablation-replace.mjs. The anchor count went from 1 to 0 and the file's blob hash changed.
  • Five files went red: Tests 12 failed | 29 passed (41), covering every zh-CN row across layout and the four app-shell surfaces.
  • After the restore, the file's blob hash equals its HEAD hash and git diff HEAD is empty.
  • The subject resolves through the vitest alias to packages/layout/src, so no build was needed between legs.

Gates

Gate Result
pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build First run red: NavigationDesigner TS2345 ×4, the item above. After the fix it exits 0.
@object-ui/app-shell build exit 0 (dist completeness … 974 emitted files verified)
type-check @object-ui/types, components, runner, layout, plugin-designer, app-shell and console all pass. Console needed five plugins built first (plugin-gantt, timeline, map, markdown, tree): its first run reported only TS2307 "cannot find module" for those packages, which means a prerequisite was missing, not that the type-check failed.
node scripts/check-changeset-presence.mjs ✅ 4 released packages, 4 changesets
pnpm changeset:check ✅
check:control-bytes, check:spec-symbols, check:i18n-keys ✅
check:new-line-citations 0 new
check:component-surface-parity report-only, exit 0
check-governed-queue-guard --test NOT GOVERNED
Lint, narrowed to the 23 changed .ts/.tsx files See below.

How the lint was narrowed:

  • The population is the files eslint.config.js lints, run as the per-package eslint . runs (inline config honoured).
  • The JSON output shows 23 files, 0 of them ignored, with 0 errors and 75 warnings. The warnings are pre-existing, on lines this diff did not write.
  • Type-aware linting is not enabled (tseslint.configs.recommended; there is no parserOptions.project or projectService), so this diff cannot change a verdict on an untouched file.
  • Under --no-inline-config there are 3 errors. All of them are on pre-existing eslint-disable-next-line react-hooks/static-components lines.

Changesets:

  • @object-ui/types minor
  • @object-ui/layout minor, with the break spelled out: the retired props, the six-argument call, and the new prop
  • @object-ui/app-shell patch
  • @object-ui/plugin-designer patch: no visible change

Acceptance notes

None of these was filed as a card. Each has no carrier unless named.

  • useNavPins stores resolveNavItemLabel(item) with no locale, so a pinned map-labelled entry's portable favourite record keeps its en text. The ruling names five mounts and this is not one of them. No measured pull: no readable app writes a map entry label.
  • plugin-designer's navigation-designer has two gaps:
    • Its preview rows and the app creation wizard call resolveNavItemLabel with no locale, so a map reads en.
    • Its inline rename starts a map label's draft empty, and committing typed text would replace the whole map with a string. The Studio's own designer avoids this with renamedLabel (objectui#11128).
    • Both behaviours predate this PR. The type change only made them visible. This is read off the source; I did not run a probe. Nothing in this repository mounts the component.
  • An empty map {} is accepted, as the spec accepts it, and renders ''. The empty-string divergence was not extended to maps, because doing so would widen a divergence from the spec that no ruling covers.
  • The pending .changeset/11201-nav-label-verbatim.md said that the three props "are still accepted, so no caller breaks" and that the sidebar is not told the viewer's locale. Both sentences were corrected in patch round 1 (ccd2a9b296), together with two label-type parentheticals in .changeset/10867-app-wizard-saves-spec-app.md.
  • ROADMAP.md (P1.15) still ticks "Wired into NavigationRenderer via optional resolveObjectLabel". It is a historical checklist and was left as is.

Notes for review

  • The file surface grew beyond the claim, in each case forced by the ruled changes: packages/plugin-designer/src/AppCreationWizard.tsx (the signature), packages/plugin-designer/src/NavigationDesigner.tsx (the type), app-shell metadata-admin/previews/navItemLabel.ts (the signature), and a docblock-only edit to AppSchemaRenderer.tsx.
  • Commit trailers follow AGENTS.md's model-free pair.

Implemented by the domain:ui seat 1 dispatch, session https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ.


Generated by Claude Code

claude added 5 commits October 1, 2026 03:10
… (objectui#11299)

`NavigationItemObject.label` was `z.string()`, so `safeValidateSchema` (what
`objectui validate` runs) refused an inline locale map label such as
`{ en: 'Accounts', 'zh-CN': '客户' }` that `@objectstack/spec`'s
`NavigationItemSchema` accepts. The mirror now takes the spec's
`I18nLabelSchema` by reference, and the TS twin `NavigationEntryItem.label`
is the spec's `I18nLabel`. The empty-string refusal is unchanged.

Pinned as a parity table against the spec's own schema, with a string control.

Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ
Co-authored-by: Claude <noreply@anthropic.com>
…locale; three inert resolver props retire (objectui#11299)

`NavigationRenderer` gains an optional `locale` prop and `resolveNavItemLabel`
a trailing `locale` argument, handed to the spec's own `resolveI18nLabel`, so
a present label written as an inline locale map renders the viewer's entry
(falling back by that resolver's order) instead of the `en` floor for everyone.
The console passes its active `language` from `UnifiedSidebar` (both mounts),
`nav:menu`, the command palette and the search results page.

`resolveObjectLabel` / `resolveDashboardLabel` / `resolveViewLabel`, inert
since objectui#11201, leave `NavigationRendererProps`, and the matching 2nd,
4th and 5th arguments leave `resolveNavItemLabel`: the signature is now
`(item, t, targetLabel, locale)`. In-tree callers updated, including
`plugin-designer`'s `AppCreationWizard`. `nav-menu-renderer`'s header comment
is made true.

Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ
Co-authored-by: Claude <noreply@anthropic.com>
…ed name (objectui#11299)

Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ
Co-authored-by: Claude <noreply@anthropic.com>
…e widened nav label type (objectui#11299)

`NavigationEntryItem.label` is the spec's `I18nLabel` now, so the inline
rename's draft can no longer hand it to the keyed-label reader as is. A string
and a keyed reference read as before; an inline locale map starts the draft
empty, which is what the keyed read answered for a map at runtime. No
behaviour change. Also adds the four changesets.

Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ
Co-authored-by: Claude <noreply@anthropic.com>
…set names both call sites (objectui#11299)

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

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 8 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/4895-retire-block-schema-family.md

  • names zod/app.zod.ts → packages/types/src/zod/app.zod.ts — edited by this change

    ⚠️ Not this family, and not touched. The live slotted record-page vocabulary — PageNodeSchema.kind === 'slotted' with slots?: PageSlotMap (packages/types/src/layout.ts), rendered by usePageAssignment / PageBlockCanvas / PageBlockInspector in @object-ui/app-shell — shares the words "block" and "slot" with the retired family and shares no declaration, type or file with it. Neither is the type: 'component' NAVIGATION item kind ({ type: 'component', componentRef }, NavigationItemSchema in zod/app.zod.ts), a different declaration in a different module.

.changeset/6335-starred-apps-nav-exclusion.md

  • names UnifiedSidebar.tsx → packages/app-shell/src/layout/UnifiedSidebar.tsx — edited by this change

    StarredApps now filters items to type !== 'nav' before rendering — the same exclusion already applied to the sidebar Favorites list in UnifiedSidebar.tsx — restoring declared-equals-enforced behaviour and making the missing locale key correct by construction. No locale packs were touched, and the FavoriteItem / RecentItem union types are unchanged.

.changeset/6349-types-internal-name-collisions-batch-2.md

  • names app.ts → packages/types/src/app.ts — edited by this change

    MenuItem — renamed to AppMenuItem in app.ts. app.ts declared a flat, all-optional interface (type?: 'item' | 'group' | 'separator', label, icon, path, href, children, badge, hidden) — the @deprecated legacy navigation item that AppComponentSchema.menu, AppAction.items and menuItemToNavigationItem read. overlay.ts declared a discriminated union, MenuCommandItem | MenuDividerItem, whose command arm requires label and whose both arms tombstone type as type?: never (objectui#6523) — precisely the key app.ts declares as a three-value enum. Re-pointing either at the other would have made an authored type: 'separator' legal on one side and a type error on the other, so the two names had to part. @object-ui/types continues to publish overlay's union as MenuItem and app's interface as AppMenuItem, exactly as before.

.changeset/7113-chart-data-model.md

  • names app.zod.ts → packages/types/src/zod/app.zod.ts — edited by this change

    .extend() with a NEW key still works and preserves the fold and the refinement; .optional(), z.discriminatedUnion, z.toJSONSchema and safeValidateSchema are all unaffected. Nothing in this repository calls the throwing combinators on either const, and the published surface already ships refined mirrors (objectql.zod.ts, complex.zod.ts, form.zod.ts, app.zod.ts), so the class is not new — but it is a real behaviour change on a published export and it belongs in the release note rather than in a reviewer's file.

.changeset/7735-zod-mirrors-stop-authoring-defaults.md

  • names app.zod.ts → packages/types/src/zod/app.zod.ts — edited by this change

    What changed. All 41 .default() call sites under packages/types/src/zod/ are removed — layout.zod.ts 22, crud.zod.ts 11, form.zod.ts 5, views.zod.ts 2, app.zod.ts 1. @object-ui/components reconciles the third face a separate finding found: flex's registration defaultProps.align seeded 'center', the value its own renderer never applies, so a designer-made node laid out differently from a hand-authored one; it now seeds 'start'.

.changeset/7918-zod-lazy-getter-identity.md

  • names app.zod.ts → packages/types/src/zod/app.zod.ts — edited by this change

    ⚠️ Also settled while locating the ten: AppMenuItemSchema has no declaration of its own — it is the barrel alias of app.zod.ts's MenuItemSchema, while the barrel's own MenuItemSchema is overlay.zod.ts's. Two different schemas, so the list really is ten entries and not nine.

.changeset/8871-page-node-refuses-breadcrumbs.md

  • names layout/src/NavigationRenderer.tsx → packages/layout/src/NavigationRenderer.tsx — edited by this change

    What was measured, on this branch's base 93127bd6f. Zero readers, with a point-access probe rather than a bare word: on that base \.breadcrumbs scores 0 tree-wide (exit 1) against \.breadcrumb\b's 12 files tree-wide (10 under packages/) as the lit control. At head the same two probes read 16 and 13 and \.breadcrumbs is exit 0 over 4 files — every hit one of this branch's own four files (this changeset, the refusal pin, layout.ts, zod/layout.zod.ts) quoting the probe string, and the pin's own exclusions put head back at exit 1. The base reading is the measurement; the head reading is this branch's echo of it. The bare word would have lied — it also names Sentry's own unrelated concept (app-shell/src/observability/sentry.ts) and appears in two comments listing UI surfaces (core/src/utils/record-title.ts, layout/src/NavigationRenderer.tsx), so a bare probe reports five readers that do not exist.

.changeset/9092-inline-locale-declared-face.md

  • names app.ts → packages/types/src/app.ts — edited by this change

    • AppComponentSchema.label (app.ts) - ObjectGridSchema.label and .description (objectql.ts) - PageNodeSchema.aria.ariaLabel (layout.ts)

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

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3585.6 KB 3607.4 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-BRrXSOzu.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.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.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) 571.63KB 136.90KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
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 (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.55KB 11.41KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.90KB 15.35KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.54KB 44.64KB
plugin-detail (index.js) 244.79KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.70KB 44.63KB
plugin-gantt (index.js) 173.03KB 43.07KB
plugin-grid (index.js) 231.53KB 63.58KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.54KB 7.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.05KB 9.67KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
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.26KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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

…in the same release (objectui#11299)

`.changeset/11201-nav-label-verbatim.md` said the sidebar is not told the
viewer's locale and that the three retired resolver props are still accepted;
`.changeset/10867-app-wizard-saves-spec-app.md` said a nav entry's `label` is
`string | undefined`. Each sentence now says what its own card did and that
objectui#11299, in the same release, changes it, pointing to that changeset.

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

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3585.6 KB 3607.4 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-BRrXSOzu.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.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.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) 571.63KB 136.90KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
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 (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 40.55KB 11.41KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.90KB 15.35KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.34KB 37.30KB
plugin-designer (index.js) 216.54KB 44.64KB
plugin-detail (index.js) 244.79KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.70KB 44.63KB
plugin-gantt (index.js) 173.03KB 43.07KB
plugin-grid (index.js) 231.53KB 63.58KB
plugin-kanban (index.js) 49.32KB 15.48KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.54KB 7.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.05KB 9.67KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
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.26KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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: ccd2a9b29600abac3a6ac04c3bb784c2f5512b77
Local-runs: none

① Derived judgments

Inputs: card objectui#11299 (body; triage 5922552691; the claim 5923826394; dev reports 5924389320 and 5924457466), PR objectui#11325 (body, its 29-file list, which equals the git diff's, and the bot comment 5924361168), the net diff against the merge-base cd5b19a7e7 read with git diff / git show, and the 43 check-runs on the head. The PR is still a draft.

Q3 — the door (@object-ui/types): right.

  • NavigationItemObject.label is stripImportedDefaults(SpecI18nLabelSchema).optional() (zod/app.zod.ts:221), the same crossing base.zod.ts:328 uses for BaseSchema.label; the helper is typed T in, T out, so this is the spec's schema by reference, not a copy. The TS twin NavigationEntryItem.label?: I18nLabel (app.ts:121) binds the spec's I18nLabel from @objectstack/spec/ui. The doc arm builds its base keys off NavigationItemObject.shape (app.zod.ts:379-381), so it takes the same label; the pin's "every arm" row covers a group heading, a nested child, an area entry and a doc entry.
  • Accept-set at label: exactly the spec's I18nLabelSchema minus the one stated divergence, the empty string. refuseEmptyNavLabel is if (item.label !== '') return; (:188), unchanged, so every map — the empty map {} included — passes through it untouched. The types pin asserts parity with the spec's NavigationItemSchema.safeParse on nine shapes (string control, map, default-keyed map, map with no en, keyed { key, defaultValue } reference refused, non-string entry refused, non-locale key refused, number refused, array refused) at NavigationItemSchema, safeValidateSchema and the strict face, refused AT label, plus the empty-string divergence row. {} is not a door row (it is asserted only on the render side, parity row [{}, 'zh-CN']); by-reference semantics make its acceptance the spec's, so nothing is wrong, but the door's acceptance of {} is unpinned.
  • No other member widened: the diff touches label and its .describe() in NavigationItemObject, and label plus its docblock in NavigationEntryItem, and nothing else in either declaration.

Q1 A — the locale (@object-ui/layout plus five console mounts): right.

  • NavigationRendererProps.locale?: string is new; resolveNavItemLabel(item, t?, targetLabel?, locale?); presentNavItemLabel hands locale straight to resolveInlineI18nLabel, which is the spec's resolveI18nLabel under that alias (NavigationRenderer.tsx:73, :408). No fallback order is restated anywhere in the diff; the layout pin's twelve-row parity table against resolveI18nLabel holds that.
  • Same locale: UnifiedSidebar resolves area labels as resolveInlineI18nLabel(area.label, language) (:552) with the very language it now passes at both mounts (:586, :711); useNavTargetLabel, which all five surfaces already use for inherited labels, reads the same useObjectTranslation().language (useNavTargetLabel.ts:90-115); nav-menu-renderer:264, CommandPalette:71 and SearchResultsPage:109 pass that language. A map label and the labels around it resolve in one locale on each surface.
  • AppSchemaRenderer: docblock only — inside the claim's "only if the same prop fits". Its NavigationRenderer mount (:470-483) passes no locale, its mobile tab bar calls resolveNavItemLabel(item) (:351), and the edited docblock states that both read the en floor. No in-tree mount of AppSchemaRenderer exists beyond its own docblock example (:514), so a locale prop would be public surface with no consumer, which is the component's own documented posture. Right; carried as an Acceptance note (a map-labelled entry rendered through AppSchemaRenderer still reads en for every viewer).

Q2 — the retirement: right, and declared truthfully.

  • The three props are gone from NavigationRendererProps and from both internal prop bags (SortableNavigationItem, NavigationItemRenderer); resolveNavItemLabel is four-ary. The layout changeset declares the compile-time break for a consumer that passes the retired props or calls with six arguments. In-tree callers at the head, every one on the new shape: CommandPalette:71, SearchResultsPage:109, nav-menu-renderer:264, metadata-admin/previews/navItemLabel.ts:78 (three args), plugin-designer/AppCreationWizard.tsx:610 (three args), useNavPins.ts:72, AppSchemaRenderer.tsx:351, NavigationDesigner.tsx ×3 (one arg), and the renderer's own five calls. In tests the only six-argument call left is the @ts-expect-error pin; navLabelOwnership.test.tsx still spreads the retired names through a Record of string to unknown bag, which its header now explains (a stale consumer's prop is ignored, not consumed). docs/, apps/, skills/ and the package READMEs name neither the props nor the old shape; ROADMAP.md:954 is a historical checklist. plugin-list/ListView.tsx:1132's resolveObjectLabel is useListFieldLabel().objectLabel under a local name, not the nav prop, and was correctly left alone. nav-menu-renderer.tsx:38-43's header now states the rule the code applies.
  • One shape the declaration does not reach, for the seat's judgment — not a false sentence, since the "Breaking for…" sentence scopes itself to the props and the six-argument call: a legacy two-argument call resolveNavItemLabel(item, objectResolver) still compiles under the new signature (a two-string-parameter function returning string is assignable to t's type under TypeScript's optional-parameter rule), and for a keyed { key, defaultValue } label the resolver would then be invoked as t. No in-tree, test, doc or changeset ever wrote that shape, and the argument had been @deprecated and inert since objectui#11201. Worth one clause in the layout changeset; not blocking.

Surface growth past the claim: forced, and behaviour-preserving where claimed.

  • NavigationDesigner.tsx labelDraftOf: the old read was @object-ui/react's resolveKeyedI18nLabel(item.label) ?? '', whose body is string → string, otherwise label.defaultValue || label.key (react/src/utils/i18n.ts:46-50), so a map answered undefined → ''. The helper answers string → string, a key / defaultValue carrier → the same function, anything else ''. Identical for every input, {} included; the TS2345 ×4 was real (the widened label no longer fits the keyed parameter). Right.
  • AppCreationWizard.tsx:610 and navItemLabel.ts:78 are the signature change with targetLabel kept in its slot; the 11196 parity test still asserts the designer text equals the runtime's answer. Right.
  • AppSchemaRenderer.tsx docblock: each new sentence checked against the head (NavigationArea.label and an entry label are both I18nLabel; NavigationRenderer takes t, resolveTargetLabel and locale; this component passes no locale). True.
  • The fourth changeset (plugin-designer) follows from two forced edits in a released package. Right.

Pins.

  • Types: 15 tests. Before the fix the 7 that fail are the three accepted-map rows of the parity table plus the four map tests of the first block, and the 8 that pass are the string controls and the refusal rows — the reported 7/8 → 15/15 is exactly the file's arithmetic. The parity table would catch the plausible wrong fixes: a hand-written string-or-record union (accepts a keyed reference and a non-locale key the spec refuses), z.any() (accepts a number and an array), a door that forgot the doc arm or the strict face.
  • Layout: the viewer's entry (zh-CN → 客户), the no-locale default, the fr fallbacks, the twelve-row parity table, the string and absent controls, the rendered group heading, entry, action, Favorites and search under locale, and the type pins. packages/layout/tsconfig.test.json includes src/**/*.test.tsx and type-check runs tsc -p tsconfig.test.json with paths: {}, so the map-labelled NavigationItem literal compiles against the rebuilt @object-ui/types d.ts, a re-added prop or argument turns a @ts-expect-error into an unused-directive error, and resolveNavItemLabel.length pins the arity at runtime. The reported ablation (locale dropped in presentNavItemLabel, zh-CN rows red, en and string controls green) is what this design predicts; it was read from the report, not re-run.
  • App-shell: one zh-CN and one en row per surface (sidebar app mount with a switchable mocked language; nav:menu found by data-block; palette; search), through a real I18nProvider. Sound.

Check-runs on the head: 43, all completed; 40 success, 3 with conclusion skipped (Test (coverage), the coverage shard matrix entry, dependabot), which is not a failure; none in_progress. Test (the roll-up), the eight shards, Test (dist pins), Type Check, Lint, Build & E2E, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Declaration, Changeset Claim Re-read and Changeset Overwrite Report are green.

② Semver level

  • .changeset/config.json holds one fixed group of 39 packages including @object-ui/types, layout, app-shell and plugin-designer. AGENTS.md (lines 268-273) rules: never declare major (CI enforces it); objectui's own breaking change is marked minor with the breaking semantics written in the body; the group's major follows @objectstack. The layout changeset's closing paragraph restates that rule correctly.
  • @object-ui/layout minor: a new public prop, three retired props, an exported function's changed arity, with the break spelled out and the before/after call given. Right (one legacy-shape clause suggested under ①).
  • @object-ui/types minor: a widened accept-set at a published zod door and a widened published type (NavigationEntryItem.label), which stops a string slot fed item.label compiling — the NavigationDesigner TS2345 is the proof. The body states the new type (string | InlineLocaleMap, the spec's own names for the two halves) and the migration (resolve through resolveNavItemLabel); it does not say "breaking" or "stops compiling". Within the rule as written; the patch-round edit to 10867 says it inside a BREAKING paragraph, the stronger form.
  • @object-ui/app-shell patch: no public surface of app-shell changes — inheritedNavEntryText's signature and the five surfaces' props are as before; what changes is the text a map label renders. Right.
  • @object-ui/plugin-designer patch: call-site and helper only; the designer and the wizard show the same text. Right.
  • Clause-②: yes on the PR and on the claim: right — a published door accepts what it refused, a public prop is added, three are removed, an exported signature changes, a published type widens.
  • Pending changesets, read at this head. The bot's eight (4895, 6335, 6349, 7113, 7735, 7918, 8871, 9092) each hold: the type: 'component' arm, the type !== 'nav' Favorites filter (UnifiedSidebar:627-635), AppMenuItem, the two superRefines, no new .default(, the barrel alias, the breadcrumb comment and the three 9092 members are all untouched. 9868 (NavigationEntryItem.label is optional; resolveNavItemLabel resolves an absent label through resolveTargetLabel), 6661 and the two 11196 files name no argument position and hold. The seven changesets added on origin/main after the merge-base mention none of this. The two corrected files: in 11201-nav-label-verbatim.md only the two bullets' sentences changed (lines 16-20), frontmatter and patch intact, both corrected sentences true at this head; in 10867-app-wizard-saves-spec-app.md only the two parentheticals changed, both true (label?: I18nLabel), both pointing at changeset files this diff adds. No pending body goes false because of this diff.

③ Boundary flags

Both reports carry no open_questions. Every dev flag and finding, answered:

  1. Surface growth (round-0 deviation): forced by the ruled changes and measured above. Accepted.
  2. Tests also ran packages/plugin-designer/: a touched package; right to include.
  3. Attribution trailer and footer deviation: AGENTS.md's model-free pair takes precedence by the repo's own rule; not a contract matter.
  4. Console type-check needed five plugin builds first: a prerequisite, not a verdict; Type Check on the head is green.
  5. useNavPins stores a label with no locale (useNavPins.ts:72): the call site documents the stored label as a placeholder the sidebar never renders (the live tree supplies the text), and 6335 keeps type: 'nav' favourites out of StarredApps; reach is the portable favourite record only, pull zero. Acceptance note; no card.
  6. NavigationDesigner: a map label's inline rename starts empty and would commit a string over the map: pre-existing (the string type merely hid it); the component is exported from plugin-designer's index but mounted nowhere in-tree; read off source with no probe. Acceptance note now; fileable only once a probe measures the component (reach unmeasured today).
  7. Empty map {} accepted, renders '': by-reference means the spec's acceptance, and extending the empty-string divergence to maps has no ruling. Acceptance note, plus a candidate question for triage on the card (does "never an empty label" extend to an empty map?). Not a defect of this PR.
  8. ROADMAP.md P1.15: a historical checklist. Nothing.
  9. 11201 changeset superseded (round 0): closed by patch round 1, correctly and minimally.
  10. 10867's "keeps its required label" (round 1): false since 9868 made the label optional — pending in the same release — and not because of this diff. Right to leave for this PR. Carrier: the seat at release time, or a one-line prose edit in whichever PR next touches .changeset/. No card.
  11. PR body stale bullet: the Acceptance-notes bullet on .changeset/11201-nav-label-verbatim.md ("I did not edit it, because it is another card's record") is stale after patch round 1 (ccd2a9b296). The dev's replacement text stands: "Corrected in patch round 1 (ccd2a9b), together with two label-type parentheticals in .changeset/10867-app-wizard-saves-spec-app.md." For the seat to apply; the body was not edited under the one-write rule.
  12. The claim's three ⛔ lines: no useI18nContext route (the host injects locale); no copy of the fallback order (pinned by parity); plugin-list's resolveObjectLabel untouched. Honoured.
  13. Reviewer's addition — the two-argument legacy call (① Q2): one clause for the layout changeset; non-blocking.

Implemented-by: claude/issue-11299-nav-label-locale-map
Reviewed-by: session_0122Knsowci76D2rBWReCzzZ

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 1, 2026 05:11
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 1, 2026 05:11
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 1, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 1, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Ejected from the merge queue: Spec Main Shape Gate (not this PR's; objectui#11330)

The merge group 44b021eb04 (this PR on main 5262f7dd3f) failed Spec Main Shape Gate, job 110229171206. Every other merge-group check that had concluded was green. GitHub dequeued the PR and dropped auto-merge.

  • Signature: step "type-check against @objectstack/spec built from objectstack main 2821e9f15b65"; first error packages/types/src/__tests__/chartconfig-aria-retired-4044.test.ts:69 TS2322. There are 5 diagnostics in 5 files. Four are packages/types retired-key pins, and the fifth is packages/runner/src/LayoutRenderer.tsx:194. This PR touches none of them.
  • Cause: objectstack d830d71f (feat(spec): a retired key's tsc error names the retirement and points at os validate (#20621) objectstack#21023) gives a retired key a branded [REMOVED] Key retired type. The same gate was green on this PR's head ccd2a9b29 against objectstack bb2eccf7ed8e, before that commit.
  • Carrier: objectui#11330, already filed. Under the standing ruling objectui#10916 (5866275396) it is a lane p0, and any merge group fails the same way until it lands.
  • Next: this seat's contract review (5925158565) and ACCEPT (5925168560) stand. Once objectui#11330's repair is on main, the seat re-arms auto-merge on this PR, unchanged. ⛔ No requeue before then.

domain:ui seat 1 · session_0122Knsowci76D2rBWReCzzZ · 2026-10-01T05:24Z

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