Skip to content

fix(cli): anchor the objectui init scaffold's manifest and drop its dead tailwind.config.js - #4063

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-3892-init-manifest-anchors
Aug 10, 2026
Merged

fix(cli): anchor the objectui init scaffold's manifest and drop its dead tailwind.config.js#4063
yinlianghui merged 1 commit into
mainfrom
claude/issue-3892-init-manifest-anchors

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#3892

Route taken: the durable gate

The card left one cell open — anchor the init manifest back to the repo item by item (generalising DEPENDENCY_ANCHORS to a third generator), or just derive the @object-ui/* ranges and bump the rest. Taken: the durable gate, and one step further than the card sketched.

The reasoning is the one platformPackageRange's own doc comment already makes for its case: a literal here is not merely a fossil risk, it is a fossil generator. Anchoring init.ts's literals to the repo would leave thirteen literals that a test tells you are stale — better than today, but it re-pays the drift every time the repo moves. So init.ts now writes no range at all. A new packages/cli/src/utils/scaffold-dependencies.ts owns the values, all three generators read them, and a repo-side bump reaches all three in one edit.

That module is where platformPackageRange(), cliVersion(), REACT_RANGE and the shared devDependency map now live; utils/app-generator.ts reads them from there instead of declaring them. A note on the dispatch's mechanism assumption, which did not hold:platformPackageRange() was not exported and could not simply be reused — it was a module-private function at app-generator.ts:200, as were cliVersion(), REACT_RANGE and APP_DEV_DEPENDENCIES. Extracting rather than exporting-in-place is what makes "one table, three generators" true of the source and not only of the test.

What changed

  • utils/scaffold-dependencies.ts (new) — the single source for every generated range. Carries the objectui#3827 / objectui#3852 rationale that came with the moved code.
  • commands/init.ts — the manifest is extracted into an exported buildInitPackageJson(name) so it can be judged without running the command, and every value in it now comes from the shared module. The tailwind.config.js write is gone, with a comment where it was saying why.
  • utils/app-generator.ts — reads the shared module; APP_DEV_DEPENDENCIES becomes SCAFFOLD_DEV_DEPENDENCIES there. No behaviour change: the two temp-app manifests are byte-identical to before.
  • __tests__/app-generator.test.tsDEPENDENCY_ANCHORS now judges three manifests. Two changes worth naming, not one:
    • the completeness check unions all three, so a range added to any generator without an anchor fails;
    • the per-range check judges every manifest that declares a name, not the first one found. routed ?? plain ?? init would have reported the exact state this PR fixes as green — the app generators were anchored while init fossilised, and a first-match read never looks at init.
  • __tests__/cli-bin.test.ts — the absence of tailwind.config.js and the derived @object-ui/* range are asserted through the real bin, because init() writes with fs directly: an absence is only meaningful where the writes happen, and only an end-to-end read proves init() writes the manifest the anchor gate judges. The init describe now scaffolds in beforeAll, so three assertions do not silently depend on test ordering.

Reverse verification

Direction predicted before running: plain red. This rule compares a generated string against a repo fact, with no schema underneath it to re-judge the same input differently, so none of objectui#5009's inversion applies.

Restoring the pre-fix literals and the tailwind.config.js write:

× sources every range from this repo instead of inventing one
AssertionError: init manifest's @object-ui/components must track this CLI's own
version: expected '^2.0.0' to be '^17.3.0'
× writes no tailwind.config.js, because v4 would never read it
AssertionError: expected true to be false
× versions the scaffold against the CLI that wrote it, not a literal
AssertionError: expected '^2.0.0' to be '^17.3.0'
Test Files 2 failed | 1 passed (3)
Tests 3 failed | 63 passed (66)

The failure message naming init manifest's is itself the evidence that the per-generator loop, rather than a first-match read, is what caught it.

A fourth test — names every range the pre-fix init manifest had drifted on — stays green under that revert by design: it feeds the anchor rule the literal map that shipped and asserts all 13 ranges are judged wrong. Thirteen, not the two the title reports, is the measured size of the drift.

Verification

pnpm --filter '@object-ui/cli^...' build # build closure first
pnpm --filter '@object-ui/cli' build
pnpm exec vitest run packages/cli/ --maxWorkers=2
Test Files 3 passed (3)
Tests 66 passed (66)
pnpm --filter '@object-ui/cli' type-check # tsc --noEmit, clean
pnpm --filter '@object-ui/cli' lint # 19 warnings, 0 errors, all pre-existing

Run from the repo root without a -- separator, per this repo's vitest guard (objectui#3288 / objectui#3378).

One interaction to flag, deliberately not fixed here

Removing tailwind.config.js means objectui doctor in a freshly scaffolded project now prints ⚠️ tailwind.config.js not found and counts an issue. That is #3891's defect, not one this PR introduces: doctor.ts:52-74 treats the absence of a v4-unread file as a problem, which is why running objectui doctor at this repo's own root already misreports today (the repo carries zero tailwind.config.* files). This PR makes the scaffold behave like the repo itself rather than creating a new case. #3891 is held to a later round and doctor.ts is untouched here.

Out-of-scope findings

Measured while verifying this card's premise, filed unassigned, not fixed here:

Both are changes to the generated sources, outside this card's completion scope, so they are standalone rather than sub-issues.


Generated by Claude Code

… dead tailwind.config.js (#3892)
`commands/init.ts` is the CLI's third package.json generator, and the only
one an external user ever sees. It sat outside both gates the other two have:
objectui#3711's version-claims scan does not read `.ts` source, and
`app-generator.test.ts`'s `DEPENDENCY_ANCHORS` covered only the two temp-app
generators. So it asked for `@object-ui/*` at `^2.0.0` against packages
publishing at 17.x — objectui#3827's defect, one generator over — while its
nine toolchain ranges drifted, two of them by a major.
Not one range is written in `init.ts` now. A new
`utils/scaffold-dependencies.ts` owns the values all three generators read:
the derived platform range, React's root pin, and the shared devDependency
map. The anchor table judges the union of the three manifests, and judges
every manifest that declares a name rather than the first one found — one
generator anchored while another fossilises is exactly the state this fixes,
and a first-match read would have reported it green.
The `tailwind.config.js` the scaffold wrote is deleted for the reason
objectui#3852 deleted the temp app's: the pipeline is v4 end to end, and v4
reads a JS config only when a stylesheet points `@config` at one. None does.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
@vercel

vercelBot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 10, 2026 2:50am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)28.1 KB350 KB
Entry fileindex-DZ5g8zc6.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)8.66KB3.13KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)7.57KB2.97KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)22.10KB4.37KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.13KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.64KB2.21KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)35.76KB9.11KB
auth (createAuthenticatedFetch.js)4.37KB1.69KB
auth (index.js)2.35KB1.07KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)4.91KB0.87KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)26.07KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.65KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)483.72KB106.71KB
core (index.js)3.04KB1.15KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)139.61KB35.99KB
fields (index.js)228.51KB56.69KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)2.65KB1.06KB
i18n (pickLocalized.js)1.70KB0.83KB
i18n (provider.js)9.48KB3.27KB
i18n (useObjectLabel.js)27.59KB6.63KB
i18n (useSafeTranslation.js)4.52KB1.96KB
layout (index.js)38.84KB10.80KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)8.75KB3.06KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.41KB1.44KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.71KB3.79KB
plugin-calendar (index.js)45.23KB12.45KB
plugin-charts (index.js)61.49KB17.48KB
plugin-chatbot (index.js)180.33KB42.79KB
plugin-dashboard (index.js)118.50KB30.66KB
plugin-designer (index.js)210.51KB42.51KB
plugin-detail (index.js)237.80KB59.48KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)113.37KB27.40KB
plugin-gantt (index.js)162.79KB39.67KB
plugin-grid (index.js)187.97KB49.79KB
plugin-kanban (index.js)48.53KB13.38KB
plugin-list (index.js)109.73KB26.55KB
plugin-map (index.js)17.00KB5.32KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)40.58KB10.58KB
plugin-timeline (index.js)26.21KB7.52KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)84.03KB20.55KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.67KB2.37KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)23.71KB7.95KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.23KB0.66KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)4.47KB2.03KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)2.71KB1.34KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@yinlianghui@claude