Skip to content

feat(app-shell): search on the Studio landing, the palette lists packages, objects and flows (objectui#11863, Q2) - #12044

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-11863-studio-search
Oct 9, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-11863-studio-search

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Fixes #11863
Clause-②: yes

What this does

Q2 of objectui#11863, search on the Studio landing, as ruled in retriage 6054672824 ("Q2 → A, with C as the fallback if no read lists flows across packages"). A read that lists flows across packages exists, so this builds A with flows. Q1 (PR #12004) and Q3 (PR #11996) have landed; Q2 is the card's last ruled item.

  • StudioLanding (apps/console/src/components/StudioRoute.tsx) mounts CommandPaletteProvider and CommandPalette scope="studio". Under the provider, the studio header draws its Search trigger through objectui#11912's existing rule, and Ctrl+K / ⌘K opens the palette. AppHeader's rule is unchanged; only its comment now names the /studio mount.
  • CommandPalette gets a declared second scope. scope="studio" takes no other prop: there is no active app, so the scope has no app navigation group, no record search, no app switching, and no full-search command (its link is /apps/APP/search). It lists three Studio groups instead:
    • Packages: fetchPackages, the list the landing and its switcher read (kernel packages already left out). Each opens /studio/PKG/data, as a landing card does.
    • Objects: useMetadata().objects, each under the package it declares (_packageId), opened at /studio/PKG/data?surface=object:NAME. An object whose package is not in the list, or that has no package, has no Studio page and is left out.
    • Flows: the metadata cache's flow list, opened at /studio/PKG/automations?surface=flow:NAME. A flow that belongs to no package opens in the package-less scope, /studio/~org/automations?surface=flow:NAME (studioOrgScopePath, objectui#11553).
  • Entries match on label or machine name through matchesPaletteQuery, the rule the palette's other entries use (objectui#11812). An object or packaged flow shows its package's name on the right. Nothing is read until the palette opens. The theme commands and the recently viewed records stay.
  • Two pack rows in all ten packs: console.commandPalette.packages and console.commandPalette.flows. The objects group reuses console.commandPalette.objects.
  • Docs: packages/app-shell/README.md (the palette section) and content/docs/guide/console.md (a "Search in Studio" row). Changeset .changeset/11863-studio-search.md. The pending Q1 changeset .changeset/11863-studio-header.md gains one append-only dated note (patch round 1, see Acceptance notes).

Contract face (Clause-② yes, widening)

  • @object-ui/app-shell, CommandPalette props: a second form, scope="studio", which takes no other prop. The in-app form (apps, activeApp, objects, onAppChange, optional dataSource) is unchanged and still required; it leaves scope out. The props type is not exported by name. No export is added or removed. The scope is declared, not inferred from a missing activeApp. A type pin in the new test holds both directions: CommandPalette scope="studio" activeApp=... and a bare CommandPalette are compile errors. The pins are two @ts-expect-error lines that the package's type-check (tsc -p tsconfig.test.json) compiles.
  • @object-ui/i18n: the exported en pack and TranslationKeys gain console.commandPalette.packages and console.commandPalette.flows. Nothing is removed.
  • Changeset: @object-ui/app-shell minor, @object-ui/i18n patch, @object-ui/console patch. That follows the Q3 changeset's precedent for added pack rows. No major.

The PM hypotheses, measured

  • H1, held on 049012b. CommandPaletteProvider was mounted only in ConsoleLayout. AppHeader asks useCommandPaletteProviderMounted(). In the reverse leg below, base StudioRoute.tsx gives "Unable to find an element by: [data-testid="action:command-palette:open"]" on /studio. So this card mounts the palette itself.
  • H2, held. The clean shape mounts in StudioLanding only, not above Home, Organizations or AI. In a real browser, /home still draws 0 desktop and 0 compact triggers on this branch. Nothing was widened or narrowed silently.
  • H3, held on 049012b. A scratch probe (deleted, never committed) put the base CommandPalette (blob 77b85897) at /studio with apps empty and activeApp null. The query search rendered the full-search item, and picking it went to /apps/undefined/search: 1 passed. This branch drops that command and every app-scoped group in the studio scope, by declaration.
  • H4: a cross-package flow read exists, so A, not C. The metadata cache's getItemsByType('flow') is the unscoped GET /api/v1/meta/flow. It is the same unscoped list that loadPackageLessSurfaces narrows, and that function's doc says "The unscoped list also carries every PACKAGED item". On the server, getMetaItems({ type }) with no package returns every package's items. The URL shapes come from StudioDesignSurface's routing on main: PILLAR_FOR_SURFACE_TYPE (object to data, flow to automations), the ?surface=TYPE:NAME deep link (DESIGNER_SURFACE_PARAM, formatSurfaceParam) and studioOrgScopePath. These were checked against a mocked API in the browser. I did not check them against a live objectstack server, because the sibling checkout is not installed or built in this container.
  • H5, fits. Eager closure under the lock: base 049012b is 3,238,404 B ("3162.5 KB gzipped across 289 of 2474 chunks (budget: 3204.6 KB, headroom: 42.1 KB)"). Branch dbca37f is 3,239,095 B ("3163.2 KB gzipped across 289 of 2474 chunks (budget: 3204.6 KB, headroom: 41.4 KB)"). The delta is +691 B, under the 3,281,467 ceiling, which was not touched. The bytes land in the index chunk (+770 B). packages-io was already in the eager PackagesPage chunk on base, so fetchPackages adds no module. i18n-locale-en grows 45,231 to 45,241 B, under its own ceiling. b6a003e changes one test docstring after dbca37f, with no source change.
  • H6. console.commandPalette.objects fits the objects heading and is reused. No row in packages/i18n fits "Packages" or "Flows" as a group heading: common.package and packagedAutomation.colFlow are singular, and console.studio.title is the product name. The designer table (metadata-admin/i18n.ts) has engine.packages.title, but the palette reads only packages/i18n through useObjectTranslation. So the PR adds two rows in all ten packs.

Verification (head b6a003e)

  • Union, every test file that imports or names CommandPalette, CommandPaletteProvider, AppHeader, StudioRoute or commandPalette (git grep -l, 59 files), under os-verify-lock: "Test Files 59 passed (59)", "Tests 515 passed (515)".

  • 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: pnpm --filter @object-ui/i18n --filter @object-ui/app-shell --filter @object-ui/console run type-check, all three script names echoed, exit 0. This ran after turbo run build --filter='@object-ui/console^...'.

  • New tests: CommandPalette.studioScope-11863.test.tsx has 10 cases: groups and exclusions, each target URL, matching, no app-scoped entry plus an app-scope CONTROL, nothing read while closed plus a CONTROL. StudioRoute.searchPalette-11863.test.tsx has 4 cases through the real studioRoutes: both triggers, click, Ctrl+K, and picking a package opens its builder. The 11912 pin's CONTROL now includes studio under a provider.

  • Reverse verification, from committed dbca37f. CommandPalette.tsx and StudioRoute.tsx were swapped to their base blobs (77b85897, 9e2098ca; anchors useStudioPaletteEntries 1 to 0 and scope="studio" 1 to 0). The run gave "Tests 13 failed | 8 passed (21)". Red: the 9 studio-scope cases (the base palette throws "Cannot read properties of undefined (reading 'filter')" with no app props) and the 4 route cases (no trigger). Green: the app-scope CONTROL and the 7 cases of the 11912 pin. The trap restored both files from HEAD: blobs 05643164, b314334d equal HEAD, and git diff HEAD is empty.

  • Gates, exit 0 each: check:i18n-keys ("Every in-scope call-site key resolves against the en pack (3313 keys)"), check:i18n-drift, check:i18n-dead-keys (report-only; neither new key listed), check:eager-locale-catalogues, check:new-line-citations ("0 new citation(s)"), check:control-bytes, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape and check:test-path-roots. Also exit 0: check:changeset-claims (report-only; 11806-login-server-unreachable.md names packages/app-shell/README.md for its sign-up table, which this PR leaves alone), check:pending-changeset-literals, check:doc-fences, check:icon-record-names, check:shell-escape-residue, check:unreferenced-sources, check:readme-exports, check:doc-snippets, check-changeset-presence ("17 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)") and check-changeset-no-major. check-governed-queue-guard --test over the 20 paths says NOT GOVERNED.

  • eslint: --no-inline-config --format json over the 17 changed ts/tsx files gives 0 errors. Each of the 5 edited files has the same warning count as its base blob through --stdin: 2, 13, 22, 11 and 13. The two new test files have 0. The population is the config's **/*.{ts,tsx} and the file count comes from the JSON output. The config is not type-aware (no parserOptions.project / projectService), so this diff cannot move any untouched file's verdict. The repo-wide pnpm lint is CI's.

  • Patch round 1, head 12f3f09. One commit, changeset text only: .changeset/11863-studio-header.md, 2 lines added and 0 removed. At that head, exit 0 each: check-changeset-presence ("17 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)"), check-changeset-no-major, check:changeset-claims (report-only, the same 11806 reading as above), check:pending-changeset-literals, check:control-bytes and check:new-line-citations ("0 new citation(s)"). The union, type-check, eslint, bundle and browser readings in this section are from b6a003e and dbca37f and were not re-run: no source or test file changed. The two bot comments on this PR were read: the changeset-claim re-read (6077690455) names .changeset/11806-login-server-unreachable.md for packages/app-shell/README.md, and its paragraph is about the sign-up table, which this PR leaves alone, so it still holds; the CI Console Performance Budget on b6a003e (6077731629) reads PASS, 3163.4 KB against 3204.6 KB.

Real browser (Chromium /opt/pw-browsers/chromium, 1440x900, vite preview of the dbca37f build on a private port, API mocked with page.route; pageErrors empty)

Where What was read
/studio desktop trigger 1, compact 1; click gives /studio?palette=1
palette, empty query headings Packages, Objects, Flows, Preferences; Acme CRM, People; Account (Acme CRM), Employee (People); Onboard Account (Acme CRM), Nightly Cleanup; three theme commands. Kernel package, sys_user and the kernel flow absent
query search 0 items: no full-search command, nothing under /apps/
query employee, picked /studio/com.acme.hr/data?surface=object%3Ahr_employee
Ctrl+K on /studio, package-less flow picked /studio?palette=1, then /studio/~org/automations?surface=flow%3Aorg_cleanup
CONTROL /apps/crm_app triggers 1 and 1; headings Objects, Preferences, Actions; search gives the full-search command
/home (untouched frame) triggers 0 and 0

Acceptance notes

  • The pending .changeset/11863-studio-header.md (Q1) ends its first paragraph with "No command palette is mounted on /studio, so the header shows no search trigger there." This PR makes that sentence false. Patch round 1 leaves the sentence as written and appends one dated note after its paragraph, in the objectui#6152 round-12 shape: from objectui#11863 Q2 (this PR) the landing mounts the command palette in its studio scope, so the header draws the search trigger and ⌘K opens it. This PR's own changeset also states the reversal in its first paragraph.
  • Bounds, by design and stated in the code: objects and flows are the published items from the metadata cache, so an item that exists only as a draft is not listed. If the package read fails, the packaged entries are left out, and the landing under the palette shows that read's error.
  • The pillar builder (/studio/PKG/TAB) mounts no palette. It is StudioDesignSurface's full-screen frame, which this claim does not hold.

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


Generated by Claude Code

claude added 5 commits October 9, 2026 08:13
… the /studio landing (objectui#11863, Q2)

`CommandPalette` takes `scope="studio"`, a frame outside every app: no
app-scoped group, no full-search command (whose link starts with
`/apps/APP`), and three Studio groups instead: the packages `fetchPackages`
lists, their objects and their flows from the metadata cache, each opening
its Studio page through the pillar's `?surface=` deep link; a package-less
flow opens in the package-less scope. Nothing is read until the palette
opens.

`StudioLanding` mounts `CommandPaletteProvider` and the palette, so the
`studio` header draws its search trigger under objectui#11912's rule and
Ctrl+K opens it. Two pack rows, `console.commandPalette.packages` and
`.flows`, in all ten packs.

Claude-Session: https://claude.ai/code/session_01CijGnfWLxTLUFcJkY2ouUw
Co-authored-by: Claude <noreply@anthropic.com>
…ole guide and a changeset (objectui#11863, Q2)

Claude-Session: https://claude.ai/code/session_01CijGnfWLxTLUFcJkY2ouUw
Co-authored-by: Claude <noreply@anthropic.com>
… `any` (objectui#11863)

Claude-Session: https://claude.ai/code/session_01CijGnfWLxTLUFcJkY2ouUw
Co-authored-by: Claude <noreply@anthropic.com>
…nt (objectui#11863)

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

github-actions Bot commented Oct 9, 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 47b1f0bb7 (merge-base with origin/main): 19 file(s) changed outside .changeset/, read against 253 pending declaration(s) that publish a body (258 pending in total). · run

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 289 chunks) 3163.4 KB 3204.6 KB
Main entry chunk (gzip) 72.5 KB 350 KB
Entry file index-MLg0vDD_.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) 19.52KB 7.19KB
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) 201.52KB 47.99KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 233.53KB 49.80KB
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.62KB 69.16KB
plugin-kanban (index.js) 52.77KB 16.56KB
plugin-list (index.js) 120.25KB 30.26KB
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.20KB 23.04KB
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

…ette mount (objectui#11863, Q2)

The pending `.changeset/11863-studio-header.md` ends its first paragraph with
"No command palette is mounted on `/studio`, so the header shows no search
trigger there." PR objectui#12044 makes that false. Append-only, in the
objectui#6152 round-12 shape: the sentence stays, and one dated note after
its paragraph says that from objectui#11863 Q2 the landing mounts the palette
in its `studio` scope, so the header draws the trigger and ⌘K opens it.

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

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 289 chunks) 3163.4 KB 3204.6 KB
Main entry chunk (gzip) 72.5 KB 350 KB
Entry file index-MLg0vDD_.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) 19.52KB 7.19KB
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) 201.52KB 47.99KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 233.53KB 49.80KB
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.62KB 69.16KB
plugin-kanban (index.js) 52.77KB 16.56KB
plugin-list (index.js) 120.25KB 30.26KB
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.20KB 23.04KB
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: 12f3f094d420c640117fd0be542806c2027941ac
Local-runs: none

Read: card objectui#11863 (body and all 22 comments, the retriage rulings 6054672824 and 6060520389, the claim 6076852010, the reports 6077723448 and 6077810878 included), PR #12044 (body, the 21-file list, the net diff against main at this head; merge base 049012bf), and the check-runs on this head. Context reads were git show at this head and at the merge base in the local objectui clone, plus one read of the sibling objectstack clone at 05c7c3fa for the spec field the Objects group rests on. Nothing was built, run or re-run.

Check-runs on this head, as last read in this act: 42 runs; 33 success, 3 skipped by design (the two coverage legs and dependabot), 6 in_progress (Test (shard 1/8), 4/8, 6/8, 7/8, 8/8, and Spec Main Shape Gate), 0 failure. Type Check, Lint, Bundle Analysis, Build & E2E, Test (dist pins), Test (shard 2/8), 3/8, 5/8, Governed Surface Queue Guard, Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report, Changeset Claim Re-read, README Export Check, Doc Snippet Type Check, Line Citation Gate, Control Byte Scan, Inert vi.mock Specifier Check are among the successes. An in-progress run is not a pass: the five open test shards are gates that may hold this diff's own pins, and the landing waits on their conclusions (the seat's act, not this record's).

① Derived judgments

  1. CommandPalette props, @object-ui/app-shell — a second accepted call shape, declared: RIGHT. The props type becomes the union AppCommandPaletteProps | StudioCommandPaletteProps, discriminated on scope (scope?: undefined on the app arm, scope: 'studio' alone on the studio arm). The app arm keeps every required prop (apps, activeApp, objects, onAppChange; dataSource optional as before), so no existing call is refused and no prop is narrowed. The type is not exported by name: chrome/index.ts exports CommandPalette only and the package index re-exports it from there. The scope is read off the discriminant (props.scope === 'studio'), never inferred from a missing activeApp. Two compile pins hold both directions (studioScopeTakesNoAppProps in CommandPalette.studioScope-11863.test.tsx: scope="studio" plus activeApp is an excess property on the studio arm; a bare CommandPalette satisfies neither arm). They are not phantom: app-shell's tsconfig.test.json includes src/**/*.test.tsx, and the package type-check script chains tsc -p tsconfig.test.json, which the CI Type Check job runs — green on this head.

  2. The runtime accept-set in the studio scope: RIGHT. The app half reads NO_APP (apps: [], activeApp: null, objects: [], no dataSource), so there is no navigation group, no app switching, and no record search (useRecordSearch is enabled only with a dataSource, unchanged). The full-search command is dropped by declaration (showFullSearch = !inStudio && matches(FULL_SEARCH_VALUE)), which closes the /apps/undefined/search link the first dev measured. Every activeApp read in the file is optional-chained (activeApp?.navigation, activeApp?.name), so null is safe. The theme commands and the recently viewed records stay; neither is app-scoped. The ruling's shape — "A, ordered after objectui#11912" — is met as the claim narrowed it: objectui#11912 closed by hiding the trigger where no palette is mounted (PR objectui#11987), so the Home, Organizations and AI frames are off this card, and a generic no-app mode would have had zero consumers; the declared studio scope is the right-sized contract. AppHeader's rule (useCommandPaletteProviderMounted) is unchanged; its diff is comment-only.

  3. The three Studio groups, each against the reads and routes on main: RIGHT, and it is A with flows, not the fallback C.

    • Packages: fetchPackages → parsePackages, which drops scope === 'system' || scope === 'cloud' (kernel) — the same list the landing and its switcher read. Each opens /studio/PKG/data; studioRoutes serves :packageId/:tab with StudioPackageBuilder.
    • Objects: useMetadata().objects (the provider's composeObjects(readType('object'), readType('view'))), each kept only when its _packageId names a package in that list, opened at /studio/PKG/data?surface=object:NAME through DESIGNER_SURFACE_PARAM and formatSurfaceParam; PILLAR_FOR_SURFACE_TYPE on main maps object to data. _packageId is a MetadataProtectionFields member that ObjectSchema and FlowSchema both spread, denormalised onto every registered item by applyProtection (read in the sibling objectstack clone at 05c7c3fa), so served objects carry it. A package-less object is left out, rightly: the package-less scope's one pillar is STUDIO_ORG_SCOPE_PILLAR = 'automations' ("Flows only"), so such an object has no Studio page.
    • Flows: getItemsByType('flow') → the provider's generic readType('flow'), the unscoped per-type read; loadPackageLessSurfaces's doc on main states "The unscoped list also carries every PACKAGED item", so one read lists flows across packages, which is the ruling's condition for A. A packaged flow opens at /studio/PKG/automations?surface=flow:NAME (flow → automations); a package-less one (isPackageLessItem: no _packageId, or the sys_metadata pseudo-package) opens at studioOrgScopePath(...), /studio/~org/automations?surface=flow:NAME (objectui#11553).
    • Matching goes through the palette's own matches (matchesPaletteQuery, objectui#11812) on label and machine name; an object or packaged flow shows its package's name on the right.
  4. Nothing is read until the palette opens: RIGHT. useStudioPaletteEntries(open && inStudio) calls its four hooks before its early return, so the Rules of Hooks hold; the fetchPackages effect is gated on enabled, and metadata.objects / getItemsByType('flow') (which start a type's fetch on first read) are touched only past that return. getTypeStatus is optional on MetadataContextValue and is called with ?.. The spinner in CommandEmpty now also follows studio.loading.

  5. The mount: RIGHT and within the claim. StudioLanding wraps its frame in CommandPaletteProvider and mounts CommandPalette scope="studio"; the studio header draws both triggers through the unchanged objectui#11912 rule, and the provider's existing keydown handler opens it on Ctrl+K / ⌘K. RequireStudioAccess and the pillar builder (StudioDesignSurface's full-screen frame) mount none — the claim names StudioLanding only. CommandPaletteProvider.tsx is untouched.

  6. @object-ui/i18n: additive only, RIGHT. console.commandPalette.packages and console.commandPalette.flows land in all ten packs (ar, de, en, es, fr, ja, ko, pt, ru, zh — ten in the diff), so TranslationKeys (typeof en) widens by two and nothing is removed; the objects heading reuses the existing console.commandPalette.objects. The new StudioRoute test reads the Packages heading by its key, and the new palette test reads it off a real en I18nProvider.

  7. @object-ui/console: no exported surface moves. StudioRoute.tsx imports two existing barrel exports; the route tree is unchanged.

  8. Prose face, read against the diff: holds. The new changeset's paragraphs (the trigger and ⌘K; what the scope leaves out and why; the three groups and their targets; label-or-machine-name matching; published items only; the props form and the two keys), the README's new palette paragraph, and the console.md "Search in Studio" row each describe what the code does at this head. README Export Check and Doc Snippet Type Check are green. The pending Q1 changeset .changeset/11863-studio-header.md gains one append-only dated note under the paragraph whose last sentence this PR makes false; the sentence itself stays, so the entry still describes Q1's own landing and the note tells the release compiler the later state. Changeset Overwrite Report and Changeset Claim Re-read are green.

  9. Tests pin the contract, not the dev's word. CommandPalette.studioScope-11863.test.tsx (10 cases: the three groups and their exclusions, each target URL, matching, no app-scoped entry with an app-scope CONTROL that renders the full-search command, nothing read while closed with an opened CONTROL, plus the two type pins) and StudioRoute.searchPalette-11863.test.tsx (4 cases through the real studioRoutes, real AppHeader, provider and palette). The 11912 pin's CONTROL leg gains studio under a provider while its no-provider leg keeps studio, which pins that the gate is the palette's presence, not the variant. Their verdicts are the test shards' (three of eight green, five in progress at the last reading).

  10. Not governed, within budget. None of the 21 paths is a governed surface (Governed Surface Queue Guard green); 873 changed lines. No eager-closure budget file is in the diff and Bundle Analysis on this head is green, so the ceiling the Q1 landing note worried about is answered by the gate, not raised.

② Semver level

.changeset/11863-studio-search.md: @object-ui/app-shell minor, @object-ui/i18n patch, @object-ui/console patch — the three released packages whose src/ the diff touches, all in the .changeset/config.json fixed group. Changeset Declaration and Changeset Bump Policy are green; no major.

  • The Clause-②: line agrees across the three carriers: the PR body's second line reads Clause-②: yes, the changeset's bold line reads Clause-②: yes (widening), and the claim 6076852010 reads Clause-②: yes. One arm, (widening), and the diff widens only: a new accepted props form on CommandPalette and two new keys in TranslationKeys. Nothing is removed, renamed or narrowed, so no migration and no ADR-0087 marker is owed.
  • yes takes at least minor: @object-ui/app-shell, the package whose component gains the form, declares it. RIGHT.
  • @object-ui/i18n at patch for two additive rows follows this card's own precedent — Q3's changeset (PR objectui#11996), PASSed at tier in record 6066636004 and adopted in 6066815198, declared the same shape for its added itemType row; the Q1 record FAILed patch there only because that diff REMOVED a key. In the fixed group the whole set takes the minor regardless, so the per-package level affects the i18n CHANGELOG heading only. RIGHT, by precedent.
  • The Q1 pending changeset's edit is body text only; its frontmatter is unchanged.

③ Boundary flags

Dev flags, report 6077723448:

  • File surface: every path is in the claim's list or its named categories, checked against the file list. CommandPaletteProvider.tsx untouched; AppHeader.tsx comment-only; the two new suites and three edited suites are "the tests beside these"; packages/app-shell/README.md and content/docs/guide/console.md are "the README or docs page that describes the palette's scope". .changeset/11863-studio-header.md matches the claim's .changeset/11863-*.md spelling and was edited on the seat's patch request. No file outside. Answered.
  • Attribution: a commit-history matter outside the diff; the diff, the changeset and the PR body carry no model identifier (grepped). Answered.
  • Scratch probe, reverse-verification script, preview server: nothing of them is in the file list. Answered.
  • open_questions: none.
  • Out of scope 1 (the Q1 changeset's sentence made false): closed by patch round 1, item ① 8 above. Answered.
  • Out of scope 2 (published items only; a failed package read leaves the packaged entries out): a stated bound, carried by the changeset ("an item that is still only a draft is not listed") and the hook's doc. One note, not escalated: on a failed fetchPackages the palette's own empty state names no reason; the landing under the same frame reports that read's error, and the degradation is visible, not a durability loss, so it stays a note. Carrier: none filed.

Dev flags, report 6077810878 (patch round 1): the worktree recreation is housekeeping; the --ack-through body refresh carried the two bot comments into the body, which reads coherently (the Acceptance note now describes the appended note). open_questions: none. Answered.

Acceptance notes in the PR body: the pillar builder mounting no palette is inside the claim (StudioLanding only). The "not checked against a live objectstack server" gap on H4 is closed by source, not left open: the URL shapes are main's own (PILLAR_FOR_SURFACE_TYPE, studioOrgScopePath, the ?surface= deep link) and the _packageId the groups key on is a spec protection field stamped on register (① 3). Live E2E (informational) is green and informational.

Rulings: Q2 → A was conditioned on a cross-package flow read; it exists (① 3), so A with flows is the right build and C is not invoked. "Ordered after objectui#11912": that card closed through PR objectui#11987 before this claim, per the claim and the Q3 ACCEPT. The lane is domain:spec per 6060520389; the claim and the review tier follow it.

Carried from the Q1 ACCEPT 6070134644, still open: the site label the /studio entry gate + the front door wordmark in homeRecoveryRedirectsFollowDeclaration-7373.test.ts is prose-stale at this head (its useHomePath() anchor still matches StudioRoute.tsx, so the gate is unaffected). That note named "Q2 of this card, or the next PR that touches that test"; this PR does not touch that test, so the carrier is the next PR that does. Escalated as a one-line note, not a defect.

Closing keyword: the PR's first line closes this card on merge. Q1 and Q3 landed as Part of, and the retriage ruled three items; this is the last. What the ruling deferred to a later card — recent objects and flows on the landing — and what it never ruled — the card's "starting templates" wording — are not owed by this PR; the seat files the deferred item if it wants it tracked.

Implemented-by: claude/issue-11863-studio-search
Reviewed-by: session_01CijGnfWLxTLUFcJkY2ouUw

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 9, 2026 09:20
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 9, 2026
Merged via the queue into main with commit 5275d1f Oct 9, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11863-studio-search branch October 9, 2026 09:38
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.

studio landing (/studio): the header holds only the product wordmark — no account menu, breadcrumb or search — and most of the page is blank

2 participants