Skip to content

feat(app-shell): the Studio landing mounts the console header, AppHeader's new studio variant (objectui#11863, Q1) - #12004

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-11863-studio-header
Oct 8, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-11863-studio-header

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Part of #11863 (Q1)

Clause-②: yes. This widens a published type of @object-ui/app-shell: the exported AppHeader component's variant prop accepts a fourth member, 'studio', beside 'app', 'home' and 'orgs'. The union (AppHeaderVariant) is not exported by name, since the package entry exports AppHeader only, so a host meets it as the prop's type, and a host that switches exhaustively over that type must handle 'studio'. A call with no variant resolves as before. No export is added or removed. It also narrows @object-ui/i18n: the exported en pack and the TranslationKeys type derived from it lose console.studio.backToHome. The changeset is minor for @object-ui/app-shell and @object-ui/i18n, and patch for @object-ui/console.

This covers only Q1 of #11863, per the retriage ruling (Q1 → A): the /studio header. Q3 landed in #11996. Q2, search on /studio, follows in a later round, so #11863 remains open.

What changed

  • packages/app-shell/src/layout/AppHeader.tsx: AppHeaderVariant gains 'studio', a sibling of 'orgs'. It draws the brand logo, then a fixed Studio crumb read from console.studio.title, as 'orgs' draws organizations.title. Everything else is the header's own, unchanged: the inbox, help and the account menu (profile, workspaces, theme, language, notification switches, sign-out). The app, home and orgs drawing is untouched; the only other edits are comments that list the frames.
  • apps/console/src/components/StudioRoute.tsx: StudioLanding mounts AppHeader with variant="studio" in the header HomeLayout uses (same classes), in place of its wordmark link. StudioPackageBuilder and the recent-package record (Q3) are untouched.
  • packages/i18n/src/locales/*.ts, all ten packs: console.studio.backToHome (the wordmark's tooltip) retires with its reader, and console.studio.title ("Studio") takes its place in the same console.studio block. "Studio" is a product name every pack already writes in Latin script inside its own Studio strings, so the value is the same in every pack.
  • packages/app-shell/README.md: the one place it lists the header's surfaces ("Platform preview badge") now names studio.
  • .changeset/11863-studio-header.md.

Why console.studio.title and not a new family: the orgs crumb reads its own frame's title row (organizations.title). The Studio front door's own rows already lived in console.studio (backToHome was its only row), so the crumb is that block's title. No new key family.

Hypotheses, measured

H1 holds. Browser drive, Chromium at 1440x900 and 390x900, the console's built dist served by vite preview (base 054fd8441 on one port, this branch on another), API mocked with page.route (signed-in admin holding studio.access, one org, two packages). Before and after, at /studio:

base 054fd8441 this branch
visible interactive elements in the header (1440) a: Back to home -> /home a: ObjectOS -> /home, button: Open inbox, button: Help & Documentation, button: AA (avatar)
header text ObjectOS PREVIEW / Studio AA
header height 40.5 px 56 px (the /home header's)
at 390 px a: Back to home -> /home a: ObjectOS -> /home, inbox, avatar (help is md and up, as on /home)
avatar menu (none) Ada Admin, Profile, My Workspaces, Create workspace, Theme, Language, In-app alerts, Desktop notifications, Log out
page errors none none

CONTROL: /home on both trees draws the same three controls at 390 px and the same menu. The provider tree: /studio (StudioRoute, then ProtectedRoute, then ConnectedShell, inside ConsoleShell) is the tree /home (ProtectedRoute, then HomeLayout) has, so AppHeader mounts with no new provider. The drive is the runtime reading of that: the real account menu, inbox and help rendered, with no page error.

H2 holds. The studio variant's brand is AppHeader's logo link, to={homePath} from useHomePath(), the line homeAffordancesFollowDeclaration-7256.test.ts already pins as "top-bar brand logo". Pinned three ways: the new AppHeader.studioVariant-11863.test.tsx runs the real useHomePath over metadata apps (no declaration: /home; isDefault on helpdesk: /apps/helpdesk); StudioRoute.test.tsx "the front door's header brand walks back to the same home the gate bounces to" drives the real route tree with a declared landing (/apps/cloud_control); and the browser drive above (/home).

H3 holds. /studio mounts no command palette, so under objectui#11987's rule the header draws no search trigger there. Pinned in AppHeader.searchTriggerNeedsPalette-11912.test.tsx, which now runs the studio variant in its no-provider case: neither trigger testid and no aria-keyshortcuts element, while the header itself drew. In the browser: 0 trigger testids, 0 aria-keyshortcuts elements, and Ctrl+K opens 0 palette overlays.

H4 holds. One new row, console.studio.title, in all ten packs; console.studio.backToHome retired from all ten with its one reader. StudioRoute.landingI18n.test.tsx now pins the crumb through the real studioRoutes and the real AppHeader (with t answering «key»), pins that no text or attribute on the landing asks for the retired key, and keeps the Han sweep with its live control. check:i18n-keys, check:i18n-drift and check:i18n-dead-keys are green (readings below). The i18n suite's untranslated-identity-4376 flagged the row in zh/ja/ko/ru/ar as identical to en, so it is allowlisted there with its reason (the product name).

H5: every consumer of AppHeaderVariant, and what it does with studio.

Consumer With studio
resolvedVariant default (appName ? 'app' : 'home') unchanged
isApp (=== 'app'): sidebar trigger, app crumbs, presence, connection dot, Design in Studio, the keyboard-shortcuts help entry, the logo hidden below sm false, so none of them; the logo shows at every width, as on home / orgs
=== 'home': the product wordmark text not drawn
=== 'orgs': the Workspaces crumb not drawn
=== 'studio' (new): the Studio crumb drawn
AppHeader.searchTriggerNeedsPalette-11912.test.tsx, Record over the union (exhaustive) gains studio: '/studio'; compiles (app-shell type-check, whose test project lists the file)
AppHeader.inboxVariant.test.tsx, two "every variant" loops (not exhaustive) studio added to both, so their titles stay true; a comment that spelled the union out now points at the type
Mount sites: HomeLayout, AiChatPage (home), OrganizationsLayout, OrganizationLayout (orgs), ConsoleLayout (app) unchanged; StudioLanding is the one studio mount

No switch over the variant exists in app-shell or apps/console. apps/console has no comparison over it.

H6: the budget. pnpm check:eager-closure, local vite build of the console under the shared verify lock, each tree on its own build:

  • base 054fd8441: 3,391,274 B gzipped (3311.8 KB, headroom 0.2 KB, 336 of 2458 chunks);
  • this branch: 3,391,194 B gzipped (3311.7 KB, headroom 0.3 KB, 336 of 2458 chunks);
  • ceiling MAX_EAGER_CLOSURE_GZIP_BYTES 3,391,484 B, untouched. Delta: -80 B. The index chunk (the one whose built code carries the studio variant prop) fell 51 B; the rest is hash-rename noise spread across chunks.

The branch build ran at 19fb06ccb; every commit after it touches test files only (git diff --stat 19fb06ccb HEAD lists five test files), none of which is in the bundle. The local base reading is 9 B above the 3,391,265 B the seat quoted from the queue-built tree.

Tests

New: AppHeader.studioVariant-11863.test.tsx (the frame, brand first then the crumb, and neither sibling variant's drawing; CONTROL: home and orgs draw no Studio crumb; the brand's target with and without a declared landing; the right-hand cluster's inbox, help and account-menu keys). Changed: StudioRoute.landingI18n.test.tsx (rewritten to pin the crumb), AppHeader.searchTriggerNeedsPalette-11912.test.tsx and AppHeader.inboxVariant.test.tsx (the studio member), and untranslated-identity-4376.test.ts (one allowlist row). StudioRoute.test.tsx, StudioRoute.lazyBuilder-11798.test.tsx and StudioRoute.recentPackage-11863.test.tsx render the landing, so they now mount the real AppHeader, whose inbox, approvals and AI-catalogue reads reached a real socket (the network-escape guard turned red on the first run); each stubs fetch with an empty answer, and the entry gate's read stays on the mocked createAuthenticatedFetch. Their wordmark wording now names the header brand. No assertion in those three changed.

Reverse verification, from the committed 555028609, run under the verify lock:

  • Leg A (behaviour): AppHeader.tsx and StudioRoute.tsx swapped to their base content (disk blobs 0de4209e7fc1 and e657a9f63280, equal to the base blobs; anchor counts console.studio.title 1 to 0, variant="studio" 1 to 0, console.studio.backToHome 0 to 1). Four files: Tests 5 failed | 29 passed (34). Red: the new test's "draws the brand, then the Studio crumb"; the landing test's crumb, retired-key and brand-link cases, and its Han sweep (it waits for the crumb first). Green: the 22 StudioRoute.test.tsx and lazy-builder cases, the two Han-instrument controls, and the new test's two variant CONTROLs, brand-target and cluster cases, which hold on any off-app variant. A trap restored both from HEAD: blobs c9bf2c535f50 and 9e2098ca83a9 equal to HEAD's, git diff HEAD empty.
  • Leg B (types): node scripts/ablation-replace.mjs (objectstack) on StudioRoute.tsx, variant="studio" to variant="studioX" (anchor 1 to 0, blob 9e2098ca83a9 to a4295167badf), then the console's tsc --noEmit against the rebuilt app-shell dist: exit 2, error TS2820: Type '"studioX"' is not assignable to type 'AppHeaderVariant | undefined'. Did you mean '"studio"'?. The suggestion names the rebuilt member, so the console read the rebuilt .d.ts. Restored: blob equal to HEAD, git diff HEAD empty.

Union, at c48ef5bf8 (git rev-parse --short HEAD, the last commit), from the worktree root under the verify lock:

  • every test that imports or names AppHeader or StudioRoute, or names a console.studio. key (39 files, from git grep -l; the new test is one of them): Test Files 39 passed (39) · Tests 330 passed (330);
  • packages/i18n/: Test Files 82 passed (82) · Tests 1316 passed | 13 skipped (1329);
  • scripts/__tests__/: Test Files 179 passed | 2 skipped (181) · Tests 5452 passed | 2 skipped (5454);
  • type-check, after building the console's dependency closure (turbo run build --filter='@object-ui/console^...', Tasks: 34 successful, 34 total): pnpm --filter @object-ui/i18n --filter @object-ui/app-shell --filter @object-ui/console run type-check, exit 0, each script name echoed (packages/i18n type-check$, packages/app-shell type-check$, apps/console type-check$). tsc --listFilesOnly shows the app-shell test project and the console project list every touched test file.

Gates

At c48ef5bf8, all exit 0:

  • pnpm check:i18n-keys: "Every in-scope call-site key resolves against the en pack (3307 keys)…";
  • pnpm check:i18n-drift: "No en value changed in this range." (1 key added, 1 removed);
  • pnpm check:i18n-dead-keys (report-only): the same 3307 pack keys and 340 candidates as on base 054fd8441; no console.studio key among them;
  • pnpm check:readme-exports: OK, 579 self-imports judged, 579 real, 0 fabricated, 0 unbuilt (after the doc-snippet build closure);
  • pnpm check:skill-examples: "Every marked skill example holds up against the built types.";
  • pnpm check:doc-snippets: "784 of 784 block(s) judged, 0 failed.";
  • node scripts/check-changeset-presence.mjs: "20 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)"; check-changeset-no-major: no major;
  • pnpm check:changeset-claims (report-only): one pending changeset names a file this edits, .changeset/11806-login-server-unreachable.md naming packages/app-shell/README.md. Its paragraph is about the sign-up table, which this does not touch, so it is still true;
  • pnpm check:pending-changeset-literals: no test source names a pending changeset;
  • pnpm check:new-line-citations: "VERDICT new-cross-file-line-citations: 0 new citation(s)";
  • pnpm check:control-bytes: OK.

Lint, narrowed: eslint --no-inline-config --format json over the 20 changed .ts/.tsx files (the count is from its JSON output): 0 errors. Each of the 19 files that exist on base carries the same error and warning counts as its base content linted the same way; the new test file has 0 errors and 13 warnings, of the two kinds its sibling 11912 test carries. The config applies to **/*.{ts,tsx} and is not type-aware (eslint.config.js sets no parserOptions.project or projectService), so this diff cannot move a verdict on an untouched file. The repo-wide pnpm lint is CI's.

Bundle Analysis on this PR: NOT MEASURED here, it had not posted when this was written.

Acceptance notes

  • packages/app-shell/src/__tests__/homeRecoveryRedirectsFollowDeclaration-7373.test.ts labels its StudioRoute.tsx site "the /studio entry gate + the front door wordmark", and its header lists StudioRoute.test.tsx the same way. The front door's link is now AppHeader's logo, which homeAffordancesFollowDeclaration-7256.test.ts already pins; the site's regex still matches the entry gate. The label is stale prose only, and the file is outside this claim, so it is not edited. Carrier: Q2 of studio landing (/studio): the header holds only the product wordmark — no account menu, breadcrumb or search — and most of the page is blank #11863 or whoever next touches that file.
  • No guide under content/docs/guide/ names the header's variants (git grep AppHeader there: two mentions, neither a variant list), so no guide changed.
  • The header's Help menu shows "This app's docs" from the navigation context's currentAppName, which no off-app frame clears; /studio inherits that from home / orgs unchanged. Read-only inference, noted, not filed.

This was implemented by the dev seat for objectui#11863 (Q1), session https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw.


Generated by Claude Code

claude added 3 commits October 8, 2026 19:34
…der's new studio variant

The /studio front door's header held only the product wordmark. It now
mounts AppHeader with variant="studio": the brand, then a fixed Studio
crumb (console.studio.title, as orgs reads organizations.title), then the
header's own right-hand cluster. The brand links to useHomePath(), as the
wordmark did. No command palette is mounted there, so no search trigger.

console.studio.backToHome retires in all ten packs with its reader.

Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
Co-authored-by: Claude <noreply@anthropic.com>
…ds locally

The /studio landing now mounts the real AppHeader, whose inbox,
approvals and AI-catalogue reads went to a real socket in the four
StudioRoute suites. Each stubs fetch with an empty answer; the entry
gate's permissions read stays on the mocked createAuthenticatedFetch.
The wordmark mentions now name the header brand.

Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
Co-authored-by: Claude <noreply@anthropic.com>
…tin packs

"Studio" is the product name every pack writes in Latin script inside its
own Studio strings, so the crumb's row is a legitimate identity with en,
recorded in the untranslated-identity allowlist with its reason.

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

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

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

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

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

.changeset/11806-login-server-unreachable.md

  • names packages/app-shell/README.md → packages/app-shell/README.md — edited by this change

    packages/app-shell/README.md documents the two new states in its sign-up table. Its useSignUpOffer recipe for hosts now asks decideSignUpOffer only once the read has answered, and answers unreachable for a failed read.

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

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 336 chunks) 3311.7 KB 3312.0 KB
Main entry chunk (gzip) 71.5 KB 350 KB
Entry file index-BIDJOPg1.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) 18.33KB 6.73KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 587.07KB 141.28KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 268.74KB 68.04KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.43KB 15.54KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 199.63KB 47.46KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.11KB 45.88KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 249.56KB 69.16KB
plugin-kanban (index.js) 52.22KB 16.38KB
plugin-list (index.js) 117.80KB 29.42KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 91.09KB 22.94KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c48ef5bf8268d2614edb715ec7d7baa48542d98d
Local-runs: none

Card #11863, Q1 (retriage 6054672824: Q1 → A; lane 6060520389; claim 6067201854; dev report 6068267508). PR #12004, draft, base main, first line Part of #11863 (Q1). Net diff against the merge base 054fd844: 22 files, +394 / −91, three commits. Check-runs on this head: 43, of which 40 success and 3 skipped by design (the two coverage legs and dependabot); none failed. Bundle Analysis is among the successes, which closes the PR body's "NOT MEASURED" line.

① Derived judgments

Each accept-set or public-surface change the diff implies, read off the diff and the head tree, and named right or wrong.

  1. AppHeader's variant prop accepts a fourth member, 'studio' (@object-ui/app-shell): the accept set of a published component prop widens. Right: it is the ruled Q1 → A. AppHeaderVariant is exported from the AppHeader module but not from the package entry — exports in the package's package.json is . and ./styles.css only, and the entry re-exports AppHeader alone — so a host meets the union only as the prop's type, as the PR body says. The only exhaustive consumer at this head is the Record in AppHeader.searchTriggerNeedsPalette-11912.test.tsx, which now names studio; no switch over the variant exists (the one switch (variant) in ActionPreview.tsx is a different variant).
  2. The studio drawing: brand logo, then PathSep and a crumb read from console.studio.title, in the branch beside the orgs crumb (organizations.title). resolvedVariant still defaults to 'app' with an appName and 'home' without; isApp stays === 'app', so the sidebar trigger, app crumbs, presence and the rest draw on no off-app variant. The app / home / orgs branches are untouched; the other edits in the file are the union and three comments. Right.
  3. No search trigger on /studio: the trigger is drawn under a CommandPaletteProvider only (objectui#11912's rule); /studio mounts none, and the 11912 test now runs the studio variant through its no-provider case. Right. Q2 (the palette outside app frames) is correctly left to a later round.
  4. StudioLanding mounts AppHeader variant="studio" (@object-ui/console), in a header whose class string is byte-equal to the one HomeLayout and OrganizationsLayout wrap their AppHeader in. The brand is the header's logo link, to={homePath} from useHomePath(), so the landing and StudioDesignSurface's Home button still name one home (objectui#7256 / finding(app-shell): app-missing / access-denied 恢复重定向仍写死 /home,应跟随声明的落地页(#7256 裁 B 的后续,先量夹具改动) #7373). studioRoutes sits under ConsoleShell (which mounts NavigationProvider) behind ProtectedRoute, the tree /home has, so the header meets no new provider gap — and StudioRoute.test.tsx, the landing i18n test and the lazy-builder test render it through the real route tree. Right. StudioPackageBuilder and the Q3 recent-package record are untouched.
  5. @object-ui/i18n widens: console.studio.title ("Studio") in all ten packs, the row the crumb reads; the identity with en in zh / ja / ko / ru / ar is allowlisted in untranslated-identity-4376.test.ts with its reason (a product name). Right: the retriage's "one crumb-label row in all ten packs". check:i18n-keys and the i18n suite are green in CI.
  6. @object-ui/i18n narrows: console.studio.backToHome leaves all ten packs. The removal is right — the retriage ruled "console.studio.backToHome retires in the same PR" — and no reader remains at this head (git grep finds only the retired-key sentinel in StudioRoute.landingI18n.test.tsx, which pins that nothing on the landing asks for it, plus two CHANGELOG entries). The exported en pack and TranslationKeys (typeof en) lose one member. Its declaration is wrong — see ②.
  7. README: the one place that lists the header's surfaces ("home / app / orgs") now names studio. Right. No guide under content/docs/guide/ lists the variants (two AppHeader mentions, a tree diagram and the notifications table). Right.
  8. Tests: the new AppHeader.studioVariant-11863.test.tsx pins the frame (brand first, then the crumb; no wordmark text, no Workspaces crumb; CONTROL: home and orgs draw no Studio crumb), the brand's target through the real useHomePath with and without a declared landing, and the right-hand cluster. The landing i18n test is rewritten to the crumb, the retired key's absence in text and attributes, the brand link, and keeps the Han sweep with its live control. The two "every variant" loops in AppHeader.inboxVariant.test.tsx gain studio. Three StudioRoute suites add a fetch stub because the real header's feeds now mount there; their assertions are unchanged. Sound.

Neutral, not findings: the studio crumb's t('console.studio.title') carries no defaultValue where the orgs sibling carries one — the key resolves in all ten packs; StudioLanding calls no setContext('home') as the four other off-app frames do — the only reader of context is UnifiedSidebar, which /studio does not mount; the Help menu's "This app's docs" reads currentAppName, which only ConsoleLayout sets and nothing clears, so /studio inherits exactly what /home and /organizations inherit (the PR's read-only inference holds; pre-existing, not this PR's).

② Semver level

Changeset .changeset/11863-studio-header.md: '@object-ui/app-shell': minor, '@object-ui/i18n': patch, '@object-ui/console': patch. All three are released packages in the fixed group with changed source; Changeset Declaration (presence) and Changeset Bump Policy (no major) are green.

  • @object-ui/app-shell: minor — right: a published prop's accept set widens, and the body names the widening and what an exhaustive host must do.
  • @object-ui/console: patch — right: a frame of the app changes; no export.
  • @object-ui/i18n: patch — wrong. The diff removes console.studio.backToHome from the exported en pack, so TranslationKeys loses a member: a narrowing of a published type. objectui's version policy (AGENTS.md §版本号策略: 「objectui 自身的破坏性变更也标 minor(在正文里写清 breaking 语义即可)」) puts a breaking change at minor with its semantics written in the body, and the two exact-shape precedents pending in the same directory do just that: 11674-shortcuts-dialog-wired-only.md (seven unused console.shortcuts.* keys leave all ten packs → '@object-ui/i18n': minor, "Clause-②: no (narrowing)") and 11676-retire-overdue-key.md (one key gone from all ten → '@object-ui/i18n': minor, a "Narrowed public surface (@object-ui/i18n)" paragraph naming en and TranslationKeys). Of the ten pending changesets that declare '@object-ui/i18n': patch at this head, none removes a key. This changeset's paragraph says the key "drops … which nothing reads any more" — true inside this repository, but it names no narrowed surface for a host. The fixed group ships at minor either way (the app-shell row drives it), so the version number is unaffected; the declaration is what is off, and no gate reads the level — this record is the only reader.
    Remedy: '@object-ui/i18n': minor, and one sentence in the i18n paragraph in the 11676 shape — the exported en pack and TranslationKeys lose console.studio.backToHome; a host that typed that key drops it.

Clause-②: line — yes in the PR body, "yes (widening)" in the changeset, yes in the claim: right, and consistent. The widening is the variant accept set. The i18n narrowing does not change the clause (a narrowing is not clause-② work, as 11674's "no (narrowing)" shows), but once the i18n row reads minor the changeset's bold line should also name the narrowing, as 11674 does.

③ Boundary flags

Dev report 6068267508 — open_questions: []. Deviations:

  • D1 (file surface: tests beyond the claim's named files) — answered: inside the claim's "the tests beside these". The three other StudioRoute suites sit beside StudioRoute.tsx; the two AppHeader suites and the new one beside AppHeader.tsx; untranslated-identity-4376.test.ts beside the locale packs in packages/i18n. No source file outside the claim is touched, and every "Not on it" path holds at this head: CommandPalette.tsx, RecentItemsProvider.tsx, useRecentItemLabel.ts, BuilderLanding.tsx, StudioDesignSurface.tsx unchanged; the app / home / orgs drawing unchanged except the union and comments.
  • D2 (console.studio.title, not a new key family) — answered: consistent with the retriage's one crumb-label row and with the orgs → organizations.title precedent.
  • D3 (attribution) — answered: all three commits carry the repository's model-free trailer pair; no model identifier in the PR body, the changeset or the diff.
  • D4 (scratch worktrees, preview servers) — housekeeping; nothing of it is in the diff or the file list.
  • Out-of-scope finding (stale site label in homeRecoveryRedirectsFollowDeclaration-7373.test.ts) — answered: the site's expression (const homePath = useHomePath()) still matches StudioRoute.tsx, whose entry gate reads the hook at this head; only the prose "the front door wordmark" is stale. Prose, outside the claim, carrier named (Q2 of studio landing (/studio): the header holds only the product wordmark — no account menu, breadcrumb or search — and most of the page is blank #11863 or the next toucher). Accepted as noted.

PR body acceptance notes — the guide check and the Help-menu inference are verified above (① item 7 and the neutral list). "Bundle Analysis NOT MEASURED" — closed: success on this head; the dev's local reading is −80 B gzip, so the eager-closure ceiling (219 B of headroom on the queue tree after Q3, per 6066798063) is not approached by this PR.

Escalation: one, the ② item. It is a one-line frontmatter change plus one sentence; the next head re-renders this record.

Implemented-by: claude/issue-11863-studio-header
Reviewed-by: session_01DBZ9bntPZ7VKyQNtJeNsgw

VERDICT: FAIL


Generated by Claude Code

…i18n narrowing, minor

The exported en pack and TranslationKeys lose console.studio.backToHome,
a narrowing of a published type, so @object-ui/i18n is declared minor
with the narrowed surface named, in the shape of the 11676 changeset.

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

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 336 chunks) 3312.1 KB 3312.0 KB
Main entry chunk (gzip) 71.5 KB 350 KB
Entry file index-BaMD8NLI.js —
Status FAIL —

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.

Which half objected:

Eager-closure half Verdict
Aggregate closure ceiling ❌ over its ceiling
Per-chunk ceilings ✅ pass
Per-chunk membership (declared packages) ✅ pass
Ceiling sensitivity (headroom) ✅ pass
Ceiling freshness (checkout vs. base branch) ✅ pass

📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 18.33KB 6.73KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 587.83KB 141.44KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 268.74KB 68.04KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.43KB 15.54KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 199.63KB 47.46KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.11KB 45.88KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 249.56KB 69.16KB
plugin-kanban (index.js) 52.22KB 16.38KB
plugin-list (index.js) 118.48KB 29.59KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 91.09KB 22.94KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 336 chunks) 3311.9 KB 3312.0 KB
Main entry chunk (gzip) 71.5 KB 350 KB
Entry file index-lT0PmvX5.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) 18.33KB 6.73KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 587.83KB 141.44KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 268.74KB 68.04KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.43KB 15.54KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 199.63KB 47.46KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.11KB 45.88KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 249.56KB 69.16KB
plugin-kanban (index.js) 52.22KB 16.38KB
plugin-list (index.js) 117.80KB 29.42KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 91.09KB 22.94KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: bd5e188f8c44795cbb0f7b7928dc890b3b07e38f
Local-runs: none

Card #11863, Q1 (retriage 6054672824: Q1 → A; lane 6060520389; claim 6067201854; dev reports 6068267508, 6068688170, 6069670107). PR #12004, draft, base main, first line Part of #11863 (Q1). Net diff against the merge base 902fbe19: 22 files, +396 / −91, five commits. Check-runs on this head: 43, of which 40 success and 3 skipped by design (the two coverage legs and dependabot); none failed. Bundle Analysis is among the successes: 3311.9 KB against the 3312.0 KB ceiling.

What moved since the earlier record (6068640274, FAIL on c48ef5bf), established from the PR's own net diff, not from the dispatch: two commits. 15aac4f0 changes .changeset/11863-studio-header.md alone (+4 / −2): the '@object-ui/i18n' row goes patch → minor, the bold clause line gains "; and one narrowing in @object-ui/i18n", and a "Narrowed public surface (@object-ui/i18n)" paragraph is added in the 11676 shape. bd5e188f merges main 902fbe19 (parents 15aac4f0 and 902fbe19); main moved 42 files between the two bases, none of them among the PR's 22, and the merge's own diff restricted to the PR's files is empty. Proof by content: git patch-id --stable of the net diff is ca0a004b on both 054fd844..15aac4f0 and 902fbe19..bd5e188f; with .changeset/ excluded, the net diff at c48ef5bf and at this head share patch-id 07ef131c. So the changeset is the only PR-own change since the earlier record, and every ① item below was re-read on this head rather than carried. What the merge changed for the gates: between the two moves the budget comment on this PR read 3312.1 KB, FAIL (15aac4f0 built against a main that then carried objectui#11999); 902fbe19 is objectui#12009's revert of that change for the ceiling, and on this head the check-run reads 3311.9 KB, PASS.

① Derived judgments

Each accept-set or public-surface change the diff implies, read off the diff and the head tree, and named right or wrong.

  1. AppHeader's variant prop accepts a fourth member, 'studio' (@object-ui/app-shell): the accept set of a published component prop widens. Right: it is the ruled Q1 → A. AppHeaderVariant is exported from the AppHeader module and not from the package entry — exports in the package's package.json is . and ./styles.css; layout/index.ts re-exports AppHeader alone, and the entry's type exports from the layout barrel are ActivityItem and PreviewBadgeProps — so a host meets the union only as the prop's type, as the PR body says. resolvedVariant still defaults to 'app' with an appName and 'home' without; isApp stays === 'app'. The only exhaustive consumer at this head is the Record in AppHeader.searchTriggerNeedsPalette-11912.test.tsx, which names studio; no switch over the variant exists in app-shell or apps/console.
  2. The studio drawing: the logo link (to={homePath} from useHomePath()), the PreviewBadge, the workspace switcher and indicator, then PathSep and a crumb read from console.studio.title, in the branch beside the orgs crumb (organizations.title). The home wordmark and the orgs crumb are not drawn; the isApp cluster (sidebar trigger, app crumbs, presence, connection dot, Design in Studio, the shortcuts entry, the logo hidden below sm) is not drawn. The app / home / orgs branches are untouched; the other edits in the file are the union and three comments. Right.
  3. No search trigger on /studio: the trigger is drawn under a CommandPaletteProvider only (objectui#11912's rule); /studio mounts none, and the 11912 test runs the studio variant through its no-provider case. Right. Q2 (the palette outside app frames) is correctly left to a later round.
  4. StudioLanding mounts AppHeader with variant="studio" (@object-ui/console) inside a header whose class string is byte-equal to the one HomeLayout and OrganizationsLayout wrap theirs in. Link, getProductName and the wordmark leave the file; useObjectTranslation stays for StudioBuilderLoading's loading line. The brand is the header's logo, so the landing and StudioDesignSurface's Home button still name one home (objectui#7256 / finding(app-shell): app-missing / access-denied 恢复重定向仍写死 /home,应跟随声明的落地页(#7256 裁 B 的后续,先量夹具改动) #7373). studioRoutes sits under ConsoleShell behind ProtectedRoute, the tree /home has, so the header meets no new provider gap, and StudioRoute.test.tsx, the landing i18n test and the lazy-builder test render it through the real route tree. Right. StudioPackageBuilder and the Q3 recent-package record are untouched.
  5. @object-ui/i18n widens: console.studio.title ("Studio") in all ten packs, the row the crumb reads; the identity with en in zh / ja / ko / ru / ar is allowlisted in untranslated-identity-4376.test.ts with its reason (a product name). Right: the retriage's "one crumb-label row in all ten packs".
  6. @object-ui/i18n narrows: console.studio.backToHome leaves all ten packs. The removal is right — the retriage ruled "console.studio.backToHome retires in the same PR" — and no reader remains at this head (git grep finds the retired-key sentinel in StudioRoute.landingI18n.test.tsx, which pins that nothing on the landing asks for it, two CHANGELOG entries, and the changeset). The exported en pack and TranslationKeys (typeof en, both re-exported from the package entry) lose one member. Now declared right — see ②.
  7. README: the one place that lists the header's surfaces now reads "home / app / orgs / studio". Right. No guide under content/docs/guide/ lists the variants (the two AppHeader mentions there are a tree diagram and the notifications table). Right.
  8. Tests: the new AppHeader.studioVariant-11863.test.tsx pins the frame (brand first, then the crumb; no wordmark text, no Workspaces crumb; CONTROL: home and orgs draw no Studio crumb), the brand's target through the real useHomePath with and without a declared landing, and the right-hand cluster. The landing i18n test is rewritten to the crumb, the retired key's absence in text and in every attribute, the brand link, and keeps the Han sweep with its live control. The two "every variant" loops in AppHeader.inboxVariant.test.tsx gain studio. Three StudioRoute suites stub fetch with an empty answer because the real header's feeds now mount there, while the entry gate's read stays on the mocked createAuthenticatedFetch; their assertions are unchanged. Sound.

Neutral, not findings, unchanged from the earlier record and re-read here: the studio crumb's t('console.studio.title') carries no defaultValue where the orgs sibling carries one (the key resolves in all ten packs); StudioLanding calls no setContext('home') (the only reader of context is UnifiedSidebar, which /studio does not mount); the Help menu's "This app's docs" reads currentAppName, which only ConsoleLayout sets and nothing clears, so /studio inherits what /home and /organizations inherit — pre-existing, not this PR's.

② Semver level

Changeset .changeset/11863-studio-header.md at this head: '@object-ui/app-shell': minor, '@object-ui/i18n': minor, '@object-ui/console': patch. All three are released packages in the fixed group with changed source; Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report and Changeset Claim Re-read are green on this head.

  • @object-ui/app-shell: minor — right: a published prop's accept set widens (yes takes at least minor), and the body names the widening and what an exhaustive host must do.
  • @object-ui/i18n: minor — right, and this answers the earlier record's FAIL reason in full. The diff removes console.studio.backToHome from the exported en pack, so TranslationKeys loses a member: a narrowing of a published type, BREAKING. objectui's version policy (AGENTS.md 版本号策略: 「objectui 自身的破坏性变更也标 minor(在正文里写清 breaking 语义即可)」) puts it at minor with the semantics written in the body, and the body now does: the new paragraph is the 11676 shape on the surface it names — the exported en pack, TranslationKeys, the one member, the fix ("has to drop it"), and the release model. The migration a breaking changeset owes is there: the FROM is the key, the TO is "drop it" — the wordmark's tooltip has no successor, since console.studio.title is a different string for a different control, not its rename. No ADR-0087 marker: objectui runs no check:adr-0087-registration, and its two precedents (11674-shortcuts-dialog-wired-only.md, 11676-retire-overdue-key.md) carry none. The version number is unchanged by the correction (the fixed group ships at minor either way); what was off was the declaration, and it is now right.
  • @object-ui/console: patch — right: a frame of the app changes; no export.

Clause-②: line — yes in the PR body (no arm), yes in the claim, yes (widening; and one narrowing in @object-ui/i18n) in the changeset. The value is right and consistent: the clause-② work is the variant accept set. The arm, read with the shared reader (scripts/pm/clause2-line.mjs, the one parser, run offline over the changeset's text), is widening — the first token inside the parenthetical — and the words after the semicolon are prose it does not read. That reader's grammar spells a diff that widens one surface and narrows another yes (narrowing), so the fixed spelling for this diff is yes (narrowing; …): one token off, and the earlier record's own remedy wording ("name the narrowing, as 11674 does") invited the compound. Consequence in this repository: none — no objectui gate reads the arm (git grep for the clause key across scripts/, .github/ and package.json at this head finds one code comment and no reader), and the breaking half is declared where it publishes, in the minor row and the "Narrowed public surface" paragraph that becomes the CHANGELOG text. Noted for the seat as a one-token change to ride on the next push, if there is one; not a reason to hold this head.

③ Boundary flags

Dev reports 6068267508 (at c48ef5bf), 6068688170 (at 15aac4f0) and 6069670107 (at bd5e188f): open_questions: [] in each. Deviations, each answered or escalated:

  • D1 (file surface: tests beyond the claim's named files) — answered: inside the claim's "the tests beside these". The three other StudioRoute suites sit beside StudioRoute.tsx; the two AppHeader suites and the new one beside AppHeader.tsx; untranslated-identity-4376.test.ts beside the locale packs. No source file outside the claim is touched, and every "Not on it" path holds at this head: CommandPalette.tsx, RecentItemsProvider.tsx, useRecentItemLabel.ts, BuilderLanding.tsx and StudioDesignSurface.tsx are unchanged; the app / home / orgs drawing is unchanged beyond the union and comments.
  • D2 (console.studio.title, not a new key family) — answered: consistent with the retriage's one crumb-label row and with the orgs → organizations.title precedent.
  • D3 (attribution) — answered: all five commits, the merge commit included (its message amended before the push, parents 15aac4f0 and 902fbe19, tree the merge of the two), carry the repository's model-free trailer pair under the Claude / noreply@anthropic.com identity; a sweep of the commit messages, the net diff and the PR body finds no model identifier.
  • D4 (scratch worktrees, preview servers, the worktree re-added at the unchanged remote head) — housekeeping; nothing of it is in the diff or the file list.
  • Round-two deviation: the PR body is stale in one sentence — the Clause-② paragraph ends "The changeset is minor for @object-ui/app-shell, patch for @object-ui/i18n and @object-ui/console", which 15aac4f0 made false; the dev does not PATCH the body under the dispatch contract. Escalated to the seat, before ready: replace that sentence. The dev's suggested text is accurate against this head: minor for @object-ui/app-shell and @object-ui/i18n, patch for @object-ui/console, the i18n row minor because the exported en pack and TranslationKeys lose console.studio.backToHome, a narrowing.
  • Round-three deviation: the merge commit's message amended before push — answered above (D3); the merge touched none of the PR's files, so the tree is the parents' merge and nothing else.
  • Round-three deviation: the dispatch's "your −80 B should hold" falsified (+88 B gzip on this base, 70 B of headroom) — answered by the head's check-run: Bundle Analysis is success at 3311.9 KB against 3312.0 KB. The dev reads the PR's content delta as +3 B gzip across three chunks (index, studioScope, i18n-locale-en) with the rest hash-rename noise in raw-identical chunks; that split is the dev's reading, not re-derived here under the read-only shape, and nothing turns on it. The ceiling proximity is main's: the Q3 landing note 6067153105 already carries "the next PR that grows it meets the ceiling", and 902fbe19 itself is objectui#12009's revert for that ceiling. Not this PR's finding; the seat carries it.
  • Out-of-scope finding (stale site label in homeRecoveryRedirectsFollowDeclaration-7373.test.ts) — accepted as noted: the site's expression still matches StudioRoute.tsx, whose entry gate reads useHomePath at this head; only the prose "the front door wordmark" is stale. Prose, outside the claim, carrier named (Q2 of studio landing (/studio): the header holds only the product wordmark — no account menu, breadcrumb or search — and most of the page is blank #11863 or the next toucher).

PR body acceptance notes: the guide check and the Help-menu inference are verified above (① item 7 and the neutral list). "Bundle Analysis NOT MEASURED" — closed: success on this head. The Changeset Claim Re-read comment on this head (6068221593) names .changeset/11806-login-server-unreachable.md for packages/app-shell/README.md; that changeset's paragraph is about the sign-up table and the useSignUpOffer recipe, which this diff's one README hunk (the Preview-badge sentence) does not touch, so it stays true and no correction is owed.

Escalations: two, both seat-side and neither on what the diff publishes — the PR-body sentence (before ready) and the changeset's arm token (one token, optional, next push if any). No change to the head is required for this verdict.

Implemented-by: claude/issue-11863-studio-header
Reviewed-by: session_01DBZ9bntPZ7VKyQNtJeNsgw

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 8, 2026 22:17
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 8, 2026 22:17
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 8, 2026
Merged via the queue into main with commit 7ebff39 Oct 8, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11863-studio-header branch October 8, 2026 22:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants