Repository navigation
feat(app-shell): the Studio landing mounts the console header, AppHeader's new studio variant (objectui#11863, Q1) - #12004
Conversation
…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>
|
changeset-claim-re-read
|
✅ Console Performance Budget
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
Size Limits
|
Contract reviewServed-tier: Card #11863, Q1 (retriage ① Derived judgmentsEach accept-set or public-surface change the diff implies, read off the diff and the head tree, and named right or wrong.
Neutral, not findings: the ② Semver levelChangeset
③ Boundary flagsDev report
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: Escalation: one, the ② item. It is a one-line frontmatter change plus one sentence; the next head re-renders this record. Implemented-by: 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>
❌ Console Performance Budget
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:
📦 Bundle Size Report
Size Limits
|
…udio-header Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
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
Size Limits
|
Contract reviewServed-tier: Card #11863, Q1 (retriage What moved since the earlier record ( ① Derived judgmentsEach accept-set or public-surface change the diff implies, read off the diff and the head tree, and named right or wrong.
Neutral, not findings, unchanged from the earlier record and re-read here: the ② Semver levelChangeset
③ Boundary flagsDev reports
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: 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: VERDICT: PASS Generated by Claude Code |
Part of #11863 (Q1)
Clause-②: yes. This widens a published type of
@object-ui/app-shell: the exportedAppHeadercomponent'svariantprop accepts a fourth member,'studio', beside'app','home'and'orgs'. The union (AppHeaderVariant) is not exported by name, since the package entry exportsAppHeaderonly, 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 novariantresolves as before. No export is added or removed. It also narrows@object-ui/i18n: the exportedenpack and theTranslationKeystype derived from it loseconsole.studio.backToHome. The changeset isminorfor@object-ui/app-shelland@object-ui/i18n, andpatchfor@object-ui/console.This covers only Q1 of #11863, per the retriage ruling (Q1 → A): the
/studioheader. 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:AppHeaderVariantgains'studio', a sibling of'orgs'. It draws the brand logo, then a fixed Studio crumb read fromconsole.studio.title, as'orgs'drawsorganizations.title. Everything else is the header's own, unchanged: the inbox, help and the account menu (profile, workspaces, theme, language, notification switches, sign-out). Theapp,homeandorgsdrawing is untouched; the only other edits are comments that list the frames.apps/console/src/components/StudioRoute.tsx:StudioLandingmountsAppHeaderwithvariant="studio"in the headerHomeLayoutuses (same classes), in place of its wordmark link.StudioPackageBuilderand 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, andconsole.studio.title("Studio") takes its place in the sameconsole.studioblock. "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 namesstudio..changeset/11863-studio-header.md.Why
console.studio.titleand not a new family: theorgscrumb reads its own frame'stitlerow (organizations.title). The Studio front door's own rows already lived inconsole.studio(backToHomewas its only row), so the crumb is that block'stitle. No new key family.Hypotheses, measured
H1 holds. Browser drive, Chromium at 1440x900 and 390x900, the console's built
distserved byvite preview(base054fd8441on one port, this branch on another), API mocked withpage.route(signed-in admin holdingstudio.access, one org, two packages). Before and after, at/studio:054fd8441a: Back to home -> /homea: ObjectOS -> /home,button: Open inbox,button: Help & Documentation,button: AA(avatar)ObjectOSPREVIEW / Studio AA/homeheader's)a: Back to home -> /homea: ObjectOS -> /home, inbox, avatar (help ismdand up, as on/home)CONTROL:
/homeon both trees draws the same three controls at 390 px and the same menu. The provider tree:/studio(StudioRoute, thenProtectedRoute, thenConnectedShell, insideConsoleShell) is the tree/home(ProtectedRoute, thenHomeLayout) has, soAppHeadermounts 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}fromuseHomePath(), the linehomeAffordancesFollowDeclaration-7256.test.tsalready pins as "top-bar brand logo". Pinned three ways: the newAppHeader.studioVariant-11863.test.tsxruns the realuseHomePathover metadata apps (no declaration:/home;isDefaultonhelpdesk:/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.
/studiomounts no command palette, so under objectui#11987's rule the header draws no search trigger there. Pinned inAppHeader.searchTriggerNeedsPalette-11912.test.tsx, which now runs thestudiovariant in its no-provider case: neither trigger testid and noaria-keyshortcutselement, while the header itself drew. In the browser: 0 trigger testids, 0aria-keyshortcutselements, andCtrl+Kopens 0 palette overlays.H4 holds. One new row,
console.studio.title, in all ten packs;console.studio.backToHomeretired from all ten with its one reader.StudioRoute.landingI18n.test.tsxnow pins the crumb through the realstudioRoutesand the realAppHeader(withtanswering«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-driftandcheck:i18n-dead-keysare green (readings below). The i18n suite'suntranslated-identity-4376flagged 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 withstudio.studioresolvedVariantdefault (appName ? 'app' : 'home')isApp(=== 'app'): sidebar trigger, app crumbs, presence, connection dot, Design in Studio, the keyboard-shortcuts help entry, the logo hidden belowsmhome/orgs=== 'home': the product wordmark text=== 'orgs': the Workspaces crumb=== 'studio'(new): the Studio crumbAppHeader.searchTriggerNeedsPalette-11912.test.tsx,Recordover the union (exhaustive)studio: '/studio'; compiles (app-shelltype-check, whose test project lists the file)AppHeader.inboxVariant.test.tsx, two "every variant" loops (not exhaustive)studioadded to both, so their titles stay true; a comment that spelled the union out now points at the typeHomeLayout,AiChatPage(home),OrganizationsLayout,OrganizationLayout(orgs),ConsoleLayout(app)StudioLandingis the onestudiomountNo
switchover the variant exists inapp-shellorapps/console.apps/consolehas no comparison over it.H6: the budget.
pnpm check:eager-closure, localvite buildof the console under the shared verify lock, each tree on its own build:054fd8441: 3,391,274 B gzipped (3311.8 KB, headroom0.2 KB, 336 of 2458 chunks);3311.7 KB, headroom0.3 KB, 336 of 2458 chunks);MAX_EAGER_CLOSURE_GZIP_BYTES3,391,484 B, untouched. Delta: -80 B. Theindexchunk (the one whose built code carries thestudiovariant 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 HEADlists 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:homeandorgsdraw 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.tsxandAppHeader.inboxVariant.test.tsx(thestudiomember), anduntranslated-identity-4376.test.ts(one allowlist row).StudioRoute.test.tsx,StudioRoute.lazyBuilder-11798.test.tsxandStudioRoute.recentPackage-11863.test.tsxrender the landing, so they now mount the realAppHeader, whose inbox, approvals and AI-catalogue reads reached a real socket (the network-escape guard turned red on the first run); each stubsfetchwith an empty answer, and the entry gate's read stays on the mockedcreateAuthenticatedFetch. 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:AppHeader.tsxandStudioRoute.tsxswapped to their base content (disk blobs0de4209e7fc1ande657a9f63280, equal to the base blobs; anchor countsconsole.studio.title1 to 0,variant="studio"1 to 0,console.studio.backToHome0 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 22StudioRoute.test.tsxand 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 fromHEAD: blobsc9bf2c535f50and9e2098ca83a9equal to HEAD's,git diff HEADempty.node scripts/ablation-replace.mjs(objectstack) onStudioRoute.tsx,variant="studio"tovariant="studioX"(anchor 1 to 0, blob9e2098ca83a9toa4295167badf), then the console'stsc --noEmitagainst the rebuiltapp-shelldist: 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 HEADempty.Union, at
c48ef5bf8(git rev-parse --short HEAD, the last commit), from the worktree root under the verify lock:AppHeaderorStudioRoute, or names aconsole.studio.key (39 files, fromgit 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);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 --listFilesOnlyshows 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 base054fd8441; noconsole.studiokey 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: nomajor;pnpm check:changeset-claims(report-only): one pending changeset names a file this edits,.changeset/11806-login-server-unreachable.mdnamingpackages/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 jsonover the 20 changed.ts/.tsxfiles (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 sibling11912test carries. The config applies to**/*.{ts,tsx}and is not type-aware (eslint.config.jssets noparserOptions.projectorprojectService), so this diff cannot move a verdict on an untouched file. The repo-widepnpm lintis 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.tslabels itsStudioRoute.tsxsite "the /studio entry gate + the front door wordmark", and its header listsStudioRoute.test.tsxthe same way. The front door's link is nowAppHeader's logo, whichhomeAffordancesFollowDeclaration-7256.test.tsalready 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.content/docs/guide/names the header's variants (git grep AppHeaderthere: two mentions, neither a variant list), so no guide changed.currentAppName, which no off-app frame clears;/studioinherits that fromhome/orgsunchanged. 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