Skip to content

fix(app-shell,plugin-detail,i18n): console, record-page and Studio UX from the 2026-10-05 cloud acceptance (objectui#11659) - #11663

Merged
hotlong merged 9 commits into
mainfrom
claude/issue-11659-console-record-ux
Oct 6, 2026
Merged

hotlong merged 9 commits into
mainfrom
claude/issue-11659-console-record-ux

Conversation

@hotlong

@hotlong hotlong commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #11659

Six console, record-page and Studio UX items from the 2026-10-05 cloud acceptance run, each executed as ruled on the card. Dispatched by the epic objectstack-ai/cloud#2636 PM loop; session https://claude.ai/code/session_64cb68a6-01c7-4dc6-acc6-ccb459c90ef2.

What changed, per ruling

  1. Cloud home primary button reads 「进入工作区」 / "Open workspace". CloudOnboardingNext (the cloud:onboarding-next widget) now resolves cloudOnboarding.openWorkspace, which replaces cloudOnboarding.openProduction in all ten packs. The hint under the button (cloudOnboarding.hintReady) was the secondary 「生产环境」 wording in the same block; it now says workspace in all ten packs. Destination unchanged: the button still navigates to the page's openProductionUrl (pinned by the new zh case).
  2. Create-workspace dialog asks for the name only. The "URL slug" field is gone from CreateWorkspaceDialog. The slug is still generated from the name by the same nameToSlug rule and sent with the create call; the owner can change it in organization settings (SettingsPage keeps its slug input). Because the user can no longer fix a taken slug in the dialog, a slug collision (ORGANIZATION_ALREADY_EXISTS, which is what better-auth's create route throws for a taken slug, or ORGANIZATION_SLUG_ALREADY_TAKEN) is retried with a short random suffix, three attempts in all. Any other refusal is shown as before. Submit is still gated on a non-empty slug, which is non-empty exactly when the trimmed name is.
  3. Backup-password reminder stays quiet in the first session. Mechanism: elapsed time. The reminder is quiet for 12 hours after the first home visit on the device, so it first shows on a later day's visit. Why this and not the alternatives:
    • the old gate counted home visits (quiet on the first mount, shown on the second), and "build the first app, come back to home" is the second mount inside the first sitting, which is exactly what the acceptance run saw;
    • sessionStorage is per tab, so a "new session" read from it fires in any second tab of the same first sitting;
    • the client holds no session id to compare (AuthClientSession carries the bearer token, and an SSO re-entry can mint a new one within one sitting).
      A device holding the old '1' flag starts the 12 hours over instead of reading it as long ago, and unavailable storage stays quiet. The component moved out of HomePage.tsx into RecoveryPasswordReminder.tsx, with the gate in recoveryReminderGate.ts; neither is exported from the package entry.
  4. Record drawer header does not cut the phone number. The highlight row is HeaderHighlight (plugin-detail), mounted by record:highlights inside the record drawer's RecordDetailView. The phone cell draws a dial icon, the number and a copy button in one inline-flex box, and the chip's truncate span cannot put an ellipsis on an inline-flex child: it clips it mid-glyph. A phone chip now takes the wide basis that email and url already take, so a whole number shows; the full value stays in the chip's hover title.
  5. Studio dashboard widgets list shows a localized widget kind. DashboardDefaultInspector's widgets list printed the stored type beside each title. It now prints the add-widget picker's name for that kind (engine.widgetPicker.type.*: zh 柱状图, en "Bar chart"), with the id on hover. An uncatalogued type still prints its id.
  6. Studio automation pillar zh copy drops flow and OFF. 自动化 · flow becomes 自动化; 默认 OFF · 审阅后再启用 becomes 默认停用 · 审阅后再启用 (matching the pillar's own 已启用 / 已停用); 可视化编排 · 点选节点配置 becomes 点选画布上的节点即可配置. The en heading drops the type too ("Automations"). The pillar is not hidden; whether it is offered is cloud#2627's call.

Premises checked

  • C1, half true. The button text on the cloud home comes from objectui (cloudOnboarding.openProduction, rendered by CloudOnboardingNext). But cloud's packages/service-tenant/src/pages/welcome.page.ts carries its own copy that QUOTES the old label, so after this lands and the pin moves, cloud's page will say one thing and the button another. Cloud was not edited. What cloud needs, all inside gettingStarted() and cardGrid():
    • Step 1's description (gs_1) ends with en Enter it with "Open Production" above., zh 从上方的「打开生产环境」进入。, ja 上の「本番環境を開く」から入ります。, es Entra desde «Abrir producción» arriba. Each should quote the new label: "Open workspace", 「进入工作区」, 「ワークスペースを開く」, «Abrir espacio de trabajo». The same sentence opens with "Your production environment is already set up" / 你的生产环境已自动就绪, which is the same operator wording.
    • Step 1's title and the hero subtitle say 打开你的环境 / "Open your environment"; optional alignment with 进入工作区.
    • The "Manage your environments" card description says 你的生产环境,以及需要时的开发副本. That card is the environments surface, where production vs dev is the point; recommended to leave it.
  • C2, true. The slug field was the workspace-slug-input in CreateWorkspaceDialog. Hiding it keeps validation and submit working (pinned), and adds collision retry because the field was the user's only way to recover from a taken slug.
  • C3, true. zh home.recoveryReminder.message is rendered only by RecoveryPasswordReminder, which HomePage mounts in both its empty and its app-list branches.
  • C4, located. Item 4: HeaderHighlight in packages/plugin-detail, via record:highlights. Item 5: the widgets list in DashboardDefaultInspector (the dashboard's default right panel in Studio). The card's middle dot was a paraphrase: the title and the type sit in separate elements.

Verification

Final head for the unions below: tests and type-check for app-shell and i18n at 5ffdd171c, plugin-detail at 992c3c2cc (the one later commit touches only a plugin-detail test file).

gate result
pnpm exec vitest run packages/app-shell/ 1010 files passed, 1 skipped; 10040 tests passed (exit 0)
pnpm exec vitest run packages/plugin-detail/ 239 files passed, 1 skipped; 2385 tests passed (exit 0)
pnpm exec vitest run packages/i18n/ 79 files passed; 1289 tests passed (exit 0)
pnpm --filter @object-ui/app-shell run type-check exit 0; tsconfig.test.json lists all five touched app-shell test files
pnpm --filter @object-ui/plugin-detail run type-check exit 0
pnpm --filter @object-ui/i18n run type-check exit 0
node scripts/check-changeset-presence.mjs exit 0
pnpm run check:new-line-citations 0 new citations over the 25 branch files, exit 0
check:i18n-keys, check:i18n-drift, check:i18n-designer-parity, check:changeset-claims, check:pending-changeset-literals, check-changeset-no-major all exit 0
eslint on the 24 touched TS files (--no-inline-config) 0 errors

Lint is a declared narrowing, not the repo-wide pnpm lint (CI runs that): the population is the branch's touched .ts/.tsx files, 24 by eslint's own JSON file count, and eslint.config.js enables no type-aware linting (no parserOptions.project or projectService), so this diff cannot move a verdict on an untouched file. The warnings on the touched files are pre-existing no-explicit-any and one pre-existing set-state-in-effect on the dialog's reset effect; two anys moved with the reminder from HomePage.tsx into its new module.

This machine is macOS, so the shared verify lock ran in its declared unlocked mode (no flock): these runs had no mutual exclusion with sibling agents.

Ablation, one per item at least

Each leg mutated the committed source through scripts/ablation-replace.mjs (anchor must hit once), ran the item's test, and restored with git checkout HEAD, proven by blob hash equal to HEAD and an empty git diff HEAD.

item mutation red
1 zh openWorkspace back to 打开生产环境 1 failed / 7 (the zh case)
2 slug input put back in the dialog 1 failed / 7 (no-slug-field case)
2 collision retry removed 1 failed / 7 (retry case)
3 gate answers true (visit-count behaviour) 2 failed / 8 (both first-sitting cases)
4 phone dropped from the wide family 1 failed / 3
5 list prints w.type again 2 failed / 8 (zh and en)
6 zh heading back to 自动化 · flow 1 failed / 4

The first retry-ablation run also failed the following case: the retry case's …Once queue survived vi.clearAllMocks() and answered the next case. The case now uses a whole implementation keyed on the slug, and the re-run fails only the retry case.

Browser

NOT VERIFIED IN BROWSER, all six items: the console needs a backend, and the dev harnesses that run without one (preview-gallery.html, record-header-preview.html) do not mount these surfaces. The item-4 mechanism was measured on a static probe that mirrors the chip's CSS (border-box sizing, 9rem vs 16rem basis, a truncate span around an inline-flex phone box): on the 9rem basis the clip box was 103px for 164px of content and the number was cut mid-glyph with no ellipsis; on the 16rem basis the box was 215px and nothing was clipped. That is a CSS measurement, not a screenshot of the product. The epic's final re-walk covers the zh-CN and en screenshots.

Acceptance notes

  • StudioDesignSurface's automation canvas header still prints a literal flow · NAME chip beside the hint when a flow is open, in every locale. It is outside this card's claimed file surface, so it is untouched here; it is the same "drop flow" family as ruling 6.
  • Link-shaped cell renderers (phone, email, url) draw an inline-flex root, so a highlight value that outgrows even the wide basis is still clipped without an ellipsis; the hover title carries it. Polish, no carrier.
  • workspace.slugLabel and workspace.slugHint are now unread in all ten packs; left in place (the dead-key report is report-only).
  • 「工作区」 now names the environment on the cloud home and in the environment home's loading text, and the organization in the create-workspace dialog. Since the environment is named after its workspace (the naming collapse), a new customer sees one thing; noted, not changed.
  • Overlap: objectui#11662 (the parallel card's PR) touches the same ten packs, on other keys. A git merge-tree of the two heads is clean.

Generated by Claude Code

hotlong and others added 9 commits October 6, 2026 08:01
…space", not "Open Production" (objectui#11659)

A new customer has exactly one environment, and "production" is
control-plane vocabulary. The `cloud:onboarding-next` ready-state button
now resolves `cloudOnboarding.openWorkspace` (zh 进入工作区) in all ten
packs, and the hint under it says "workspace" instead of "production
environment". The destination is unchanged: the page's sso-open URL.

Claude-Session: https://claude.ai/code/session_64cb68a6-01c7-4dc6-acc6-ccb459c90ef2
Co-authored-by: Claude <noreply@anthropic.com>
…bjectui#11659)

The URL slug field is gone from `CreateWorkspaceDialog`: the slug is
generated from the name by the same rule as before, still sent with the
create call, and stays editable in organization settings. A slug
collision is no longer the user's to fix here, so it is retried with a
short random suffix (three attempts in all); other refusals show as
before.

Claude-Session: https://claude.ai/code/session_64cb68a6-01c7-4dc6-acc6-ccb459c90ef2
Co-authored-by: Claude <noreply@anthropic.com>
…s first session (objectui#11659)

The reminder was gated on a home-visit count: quiet on the first home
mount, shown on the second. A new user who builds their first app and
returns to home makes that second mount inside the first sitting, which
is where the acceptance run saw it. The gate now reads elapsed time:
quiet for 12 hours after the first home visit on the device, so it first
shows on a later day's visit. The component moves to its own module so
it can be tested directly.

Claude-Session: https://claude.ai/code/session_64cb68a6-01c7-4dc6-acc6-ccb459c90ef2
Co-authored-by: Claude <noreply@anthropic.com>
…hone number (objectui#11659)

The phone cell draws its icon, number and copy button in one
inline-flex box, which the chip's `truncate` span clips without an
ellipsis: a 9rem chip read 0574-876 for 0574-8765-4321. A `phone` chip
now takes the wide basis `email` and `url` already take.

Claude-Session: https://claude.ai/code/session_64cb68a6-01c7-4dc6-acc6-ccb459c90ef2
Co-authored-by: Claude <noreply@anthropic.com>
…ind, not its type id (objectui#11659)

`DashboardDefaultInspector` printed the stored `type` beside each widget
title (按客户状态统计数量 · bar). It now prints the name the add-widget
picker shows for that kind, in the designer's language, with the id on
hover; an uncatalogued type still prints its id.

Claude-Session: https://claude.ai/code/session_64cb68a6-01c7-4dc6-acc6-ccb459c90ef2
Co-authored-by: Claude <noreply@anthropic.com>
…m its zh copy (objectui#11659)

The list heading read 自动化 · flow and the top bar 默认 OFF · 审阅后再启用.
They now read 自动化 and 默认停用 · 审阅后再启用, and the canvas hint
drops 可视化编排. The en heading drops the type as well.

Claude-Session: https://claude.ai/code/session_64cb68a6-01c7-4dc6-acc6-ccb459c90ef2
Co-authored-by: Claude <noreply@anthropic.com>
…wn module (objectui#11659)

`RecoveryPasswordReminder.tsx` exported its gate and storage keys beside
the component, which `react-refresh/only-export-components` flags. The
gate moves to `recoveryReminderGate.ts`; behaviour is unchanged.

Claude-Session: https://claude.ai/code/session_64cb68a6-01c7-4dc6-acc6-ccb459c90ef2
Co-authored-by: Claude <noreply@anthropic.com>
…the next case (objectui#11659)

Its `mockRejectedValueOnce(...).mockResolvedValueOnce(...)` queue
survives `vi.clearAllMocks()`: with the retry ablated, the unconsumed
resolved value answered the next case's first call and failed it too.
A whole implementation keyed on the slug fails only the case it belongs
to.

Claude-Session: https://claude.ai/code/session_64cb68a6-01c7-4dc6-acc6-ccb459c90ef2
Co-authored-by: Claude <noreply@anthropic.com>
…asting it (objectui#11659)

`{ fields: ['phone', 'status'] }` satisfies the renderer's declared
schema prop, so the `as any` the new pin carried was an avoidable lint
warning.

Claude-Session: https://claude.ai/code/session_64cb68a6-01c7-4dc6-acc6-ccb459c90ef2
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 331 chunks) 3319.0 KB 3330.4 KB
Main entry chunk (gzip) 152.3 KB 350 KB
Entry file index-LMiW4BTo.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) 17.22KB 6.37KB
app-shell (runtime-config.js) 22.52KB 7.86KB
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) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.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) 574.72KB 137.94KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 232.57KB 64.51KB
fields (index.js) 262.75KB 66.62KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 35.66KB 9.49KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.18KB 11.71KB
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.17KB 15.46KB
plugin-charts (index.js) 84.26KB 23.05KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 143.58KB 38.81KB
plugin-designer (index.js) 231.41KB 48.84KB
plugin-detail (index.js) 247.23KB 65.04KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.09KB 45.89KB
plugin-gantt (index.js) 179.16KB 45.06KB
plugin-grid (index.js) 238.48KB 65.51KB
plugin-kanban (index.js) 52.17KB 16.37KB
plugin-list (index.js) 116.72KB 29.10KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 38.80KB 11.71KB
plugin-tree (index.js) 14.51KB 5.15KB
plugin-view (index.js) 90.23KB 22.73KB
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.08KB 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

@hotlong
hotlong marked this pull request as ready for review October 6, 2026 00:56
@hotlong
hotlong enabled auto-merge October 6, 2026 00:56
@hotlong
hotlong added this pull request to the merge queue Oct 6, 2026
Merged via the queue into main with commit f9f4a62 Oct 6, 2026
45 checks passed
@hotlong
hotlong deleted the claude/issue-11659-console-record-ux branch October 6, 2026 01:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

1 participant