Skip to content
This repository was archived by the owner on Oct 26, 2026. It is now read-only.

tooling(objectui-readiness 5): the workspace's own files come from workspace-root.mjs, so tests and the CLI generator read the same layout under the monorepo's ui/ (objectstack#25733) - #13498

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-cwrm71
Oct 25, 2026

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Part of objectstack#25733 (child 2e of the epic). Part of objectstack#25677.
Clause-②: no
Metadata-protocol: none

  • Adds no product-specific widget, dialog, hook or string (see AGENTS.md › Product-neutral renderer)

Why

Standalone, the objectui tree is the pnpm workspace root. Under the objectstack monorepo it is ui/, and the workspace root (pnpm-workspace.yaml, node_modules/.pnpm) is one level up. ui/node_modules then holds only the ui root package's own links. There is no ui/node_modules/.pnpm and no ui/pnpm-workspace.yaml.

Measured on a scratch worktree of the objectstack verification head cfa1676afc, with this tree applied under ui/: 12 tests went red across 5 files, before this change.

  • check-doc-snippet-types.test.ts, 2 reds. The UNDECLARED and ROOT-DECLARED controls looked for TREE/node_modules/.pnpm, so both read "NOT INSTALLED", and check:doc-snippets exited 2.
  • vite-objectstack-client-dist.test.ts, 5 reds. The installed client is a workspace link to packages/client, not a store symlink, and that link carries its own node_modules.
  • vite-objectstack-spec-dist.test.ts, 2 reds, and vite-objectstack-workspace.test.ts, 2 reds. The checkout's own console has the framework's packages/ three levels up, so its tests are the merged arm and not the baseline literals.
  • packages/cli app-generator.test.ts, 1 red. The in-workspace case used the checkout root as "a workspace root", and under ui/ it is a member.

What changes

  • scripts/workspace-root.mjs gains virtualStorePath(root = uiRoot()), which is the workspace root's node_modules/.pnpm. It throws, like workspaceRoot(), when no workspace governs the tree. findInstalledCopy in check-doc-snippet-types.mjs reads it, and returns null with no workspace. The relative path it reports reads ../node_modules/.pnpm/… under ui/.
  • @object-ui/cli currentContext() returns isMonorepo: isWorkspaceRoot(cwd). That is the predicate dev, serve and build already use (utils/workspace-vite.ts, objectui#3890), and it is the same expression the function used to spell inline, existsSync(join(cwd, 'pnpm-workspace.yaml')). See the proposal below.
  • Console vendor-chunk tests. The standalone arm is read off a tmp fixture (TMP/objectui/apps/console, nothing beside it), and the merged arm off the existing monorepo-shaped fixture. The checkout itself is asked only which arm its own layout names, with isNestedWorkspace() as the independent witness. vite-objectstack-spec-dist.test.ts compares the config's vendor test with the arm the layout selects, and pins the baseline literal separately. The regexes in scripts/vite-objectstack-workspace.ts are unchanged.
  • Client-dist test. The dependency walk through a pnpm symlink is held on a fixture store (node_modules/.pnpm/@objectstack+client@1.0.0/node_modules/{client,core} and a console symlink). The out-of-tree copy leaves the package's own node_modules behind.
  • check:ui-root-lookups gains the workspace-file-at-ui-root shape. It fires on a join/resolve (or a template led by a substitution) that names pnpm-workspace.yaml or node_modules/.pnpm and whose base resolves to the objectui tree. The base can be uiRoot(), UI_ROOT, __dirname / import.meta.dirname / fileURLToPath(import.meta.url) climbed to the tree, or a const or parameter default bound to one of those. It fires once, where the path first names the file. It adds 14 self-test cases, 51 in total. The allowlist entry for app-generator.ts is removed, because it would read stale.

Proposal: what "runs at a workspace root" means for currentContext()

currentContext() is product code, so its meaning is written in its doc comment and proposed here. "Runs at a workspace root" means cwd itself holds pnpm-workspace.yaml, the same predicate dev, serve and build use to skip install and to alias. A workspace member gets the standalone map. That covers a user's package inside their own monorepo, and objectui under ui/. The generator does not walk up, because walking up would hand a member the empty in-workspace maps, and no alias table backs those maps at a member's cwd.

A user's project behaves exactly as before. With the file in cwd it gets the in-workspace map, and without it the standalone map, because the expression is the same. Two tests pin it, both from fixtures: the in-workspace case runs from a fixture workspace root, and a new case runs from a fixture member and expects the standalone map.

Reverse check

The new gate shape, run on the pristine #13491 head (f8fa141), fires at exactly the two sites this PR changes, and nowhere else outside the allowlist:

  • scripts/check-doc-snippet-types.mjs findInstalledCopy, workspace-file-at-ui-root, join(root, 'node_modules', '.pnpm')
  • packages/cli/src/utils/app-generator.ts currentContext, marker-probe, existsSync(join(cwd, 'pnpm-workspace.yaml'))

Verification

objectui standalone, on this branch (main 6c3340c plus this commit, after pnpm run setup). Every command exited 0:

pnpm check:ui-root-lookups · node scripts/check-ui-root-lookups.mjs --self-test (51 cases) · pnpm check:new-line-citations · node scripts/check-changeset-presence.mjs · node scripts/check-changeset-overwrite.mjs · pnpm check:changeset-claims · pnpm type-check:scripts · pnpm check:test-path-roots · pnpm check:pre-install-import-graph · pnpm check:control-bytes · pnpm exec eslint over the changed files (0 errors) · pnpm --filter @object-ui/cli test (30 files, 416 tests).

pnpm exec vitest run scripts/__tests__ was run whole in three shards (--shard=K/3, under the verify lock): 196 files passed, 5787 tests. Two files were skipped by their own IS_CHILD conditions.

Merged layout. I used a scratch worktree of the objectstack verification head cfa1676afc. I installed it and ran the CI build of the seven @objectstack packages, plus the @object-ui closure check:doc-snippets builds. Then I applied #13491's diff and this diff under ui/ with git apply --directory=ui. This diff applies to the landed main unchanged; it is byte-identical apart from blob ids.

  • The 7 touched script test files: 293/293 passed.
  • vitest run --root . packages/cli/src/__tests__/app-generator.test.ts: 48/48 passed.
  • pnpm --dir ui run check:ui-root-lookups: OK, 6 allowlist entries live.
  • pnpm --dir ui run check:doc-snippets: exit 0, 811 blocks compiled. Both controls are live and installed at ../node_modules/.pnpm/….
  • Control, before this diff: the same 7 files plus the CLI test showed the 12 reds above, and check:doc-snippets exited 2 with both controls "NOT INSTALLED".

Bound of the new gate shape

Two of the three classes this card fixes are not syntactic joins: the CLI test treating the checkout root as a workspace root, and the vite sibling-directory probe. They are out of the gate's reach, and so are a path assembled through let or across files. The gate header states this bound.

Out of scope

Pin scripts (objectstack#25711), spec drift (objectstack#25727), QUICK_REFERENCE (objectstack#25697), dependency versions (objectstack#25726), .github/**.

🤖 Generated with Claude Code

https://claude.ai/code/session_018KHW3U8PmJue4aVvtXutRb

…rkspace-root.mjs, so tests and the CLI generator read the same layout under the monorepo's ui/ (objectstack#25733)

Standalone, the objectui tree is the pnpm workspace root. Under the
objectstack monorepo it is ui/, and the workspace root (with
pnpm-workspace.yaml and node_modules/.pnpm) is one level up.

- scripts/workspace-root.mjs gains virtualStorePath(): the workspace
  root's pnpm virtual store. check-doc-snippet-types' findInstalledCopy
  reads it, so the UNDECLARED and ROOT-DECLARED controls stay live
  under ui/.
- @object-ui/cli currentContext() asks isWorkspaceRoot(cwd), the
  predicate dev, serve and build already use. Same expression, so a
  user's project behaves as before; a fixture workspace pins the
  in-workspace map and a member pins the standalone map.
- The Console vendor-chunk and client-dist tests read their standalone
  and merged arms off fixture layouts; the checkout is asked only which
  arm its own layout selects. The vite regexes are unchanged.
- check:ui-root-lookups gains the workspace-file-at-ui-root shape: a
  join of pnpm-workspace.yaml or node_modules/.pnpm onto the objectui
  tree.

Part of objectstack#25677.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 85 chunks) 3037.1 KB 3496.0 KB
Main entry chunk (gzip) 80.8 KB 350 KB
Entry file index-BEqWKFim.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 (buildIdentity.js) 0.80KB 0.49KB
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 21.41KB 7.90KB
app-shell (runtime-config.js) 24.52KB 8.48KB
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) 45.59KB 12.03KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 20.67KB 6.12KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.74KB 3.94KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.62KB 0.83KB
auth (authStyles.js) 5.27KB 1.86KB
auth (createAuthClient.js) 42.84KB 11.66KB
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) 8.84KB 3.61KB
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.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
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) 615.71KB 150.71KB
core (index.js) 10.51KB 4.16KB
create-plugin (index.js) 27.92KB 9.50KB
data-objectstack (index.js) 253.04KB 70.68KB
fields (index.js) 296.40KB 75.98KB
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.17KB 11.96KB
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) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
permissions (ServerReadScope.js) 4.03KB 1.82KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.99KB 0.43KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.91KB
plugin-calendar (index.js) 55.31KB 16.16KB
plugin-charts (index.js) 88.30KB 24.33KB
plugin-chatbot (core.js) 5.22KB 2.18KB
plugin-chatbot (index.js) 238.05KB 56.30KB
plugin-dashboard (index.js) 147.15KB 39.95KB
plugin-designer (index.js) 232.83KB 50.01KB
plugin-detail (index.js) 268.92KB 71.56KB
plugin-editor (index.js) 2.21KB 1.04KB
plugin-form (index.js) 185.46KB 48.48KB
plugin-gantt (index.js) 178.19KB 45.06KB
plugin-grid (index.js) 277.62KB 77.39KB
plugin-kanban (index.js) 64.27KB 19.81KB
plugin-list (index.js) 149.51KB 39.15KB
plugin-map (index.js) 27.54KB 9.10KB
plugin-markdown (index.js) 13.79KB 4.79KB
plugin-report (index.js) 46.42KB 13.06KB
plugin-timeline (index.js) 39.74KB 12.00KB
plugin-tree (index.js) 15.25KB 5.38KB
plugin-view (index.js) 93.37KB 24.00KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
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) 129.13KB 42.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.71KB 2.11KB
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.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
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 (product-blocks.js) 0.20KB 0.19KB
types (record-approval-decision.js) 0.20KB 0.20KB
types (record-components.js) 0.20KB 0.19KB
types (record-panels.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.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
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

Dispatch seat record. ACCEPTed on card objectstack#25733. check-governed-merges --pr objectstack-ai/objectui#13498 on the final file list: NOT governed (0 of 14 paths in the register; 7 surfaces govern objectui), 488 changed lines (+436/−52) under the 3000-line human-merge threshold. CI 36/36 check runs completed on head 33dffb76d2, none failed. The currentContext() meaning proposed in the body (cwd itself holds pnpm-workspace.yaml; a member gets the standalone map) is accepted. Flipping ready and arming auto-merge (ordinary queue landing). No reviewer requested; this lands on the dispatch seat's verdict.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 25, 2026 04:06
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 25, 2026 04:06
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 25, 2026
Merged via the queue into main with commit bba96ce Oct 25, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-cwrm71 branch October 25, 2026 04:17
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants