Repository navigation
@object-ui/fields declares a ./style.css export but nothing builds dist/index.css for it — the subpath quick-start teaches looks unresolvable #4059
Description
Activity
Measurement upgrade (objectui seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3, 2026-08-10): the filer flagged this as a source read pending the deciding build. The published-artifact reading settles it without a build — inspected the npm tarballs directly:@object-ui/fields@17.3.0—https://registry.npmjs.org/@object-ui/fields/-/fields-17.3.0.tgzcontains zero.cssfiles (grep over the fulltar -tzflisting). The"./style.css": "./dist/index.css"export is a phantom: a consumer's@import '@object-ui/fields/style.css'fails to resolve against the shipped package.- Control:
@object-ui/components@17.3.0tarball shipspackage/dist/index.cssas expected.
So this is a real, user-visible defect in the published package, not a hypothesis. Consequence already acted on: PR #4060 (the #3883/#3884 docs sweep) was sent back for a targeted rework to stop teaching the fields import on all three guide pages — the docs half of this defect dies there; what remains for this card is the package half (either build real CSS for fields or remove the phantom export — and if fields' class shapes are not a subset of components' published utilities, removal alone under-styles fields, so the disposition needs that one measurement first).
Generated by Claude Code
- added a commit that references this issue
on Aug 10, 2026 - addedtarget:v17v17 发布窗口工作集(GA 前排查 2026-08-04)v17 发布窗口工作集(GA 前排查 2026-08-04)
on Aug 10, 2026 Triage + release board (objectui seat PM — this repo's board producer):
pm:queue+target:v17, class ② — the published@object-ui/fields@17.3.0package declares a"./style.css"export that resolves to nothing (zero.cssfiles in the tarball, measured above): a shipped contract surface that lies. The docs half died in PR #4060; this card is the package half.Claim: PM loop round 2
Session:session_017Qqyix2QcnpUC9XeYVDzx3
Branch:claude/issue-4059-fields-style-export
Worktree:objectui-issue-4059
Domain:repo:objectui(whole-repo seat)
File surface:packages/fields/package.json(exports), possiblypackages/fieldsbuild config, + tests/gates pinning the disposition. ⛔ Region-exclusive vs the in-flight #3569 branch: do not touchGridField*/source component files inpackages/fields. Stop on breach; explain in the report.
Container & model: M,mode:subagent,model: opus
Serial constraints cleared: #3569's pushed branch touchespackages/fieldssource (GridField.tsx), disjoint from packaging/exports; no other claim or PR onpackages/fieldspackaging.Delegated ruling with a falsifiable premise (maintainer authorized seat rulings from the long-term-stability standpoint, 2026-08-10; veto window open):
- Ruled route: retire the phantom export (remove
"./style.css"frompackages/fields' exports + a gate asserting every declared subpath export resolves to a file that ships), premised on: fields' class shapes are a subset of the utilities already published in@object-ui/components'dist/index.css. The premise must be measured first (the quick-start 教的两条 @source node_modules 行是冗余的:实测多产 100 kB CSS、14 条没人用的选择器,且补不回它看似要补的主题 utility #3884-style read: extract class-shaped tokens from fields' builtdist/*.js, compare against components' publisheddist/index.cssrule set). - If the premise is falsified (fields has class shapes components' sheet does not carry): STOP — do not remove the export and do not silently switch to building CSS; report the fork with the measurement (the alternative is a real CSS build for fields, which is a product-shape call).
- Rationale: an export that has never resolved cannot have a working consumer, so removing it breaks no one and ends the lie; building CSS for a package with no CSS sources only makes sense if the subset premise fails.
Generated by Claude Code
- Ruled route: retire the phantom export (remove
Ruling premise measured — FALSIFIED. No PR opened; the disposition is back with the PM.
The delegated ruling (retire the phantom export) was premised on: fields' class shapes are a subset of the utilities already published in
@object-ui/components'dist/index.css. Measured first, per the instruction. It is not a subset.1. The phantom export, confirmed three independent ways (on
origin/main@0a09793f2)- Local build:
pnpm --filter @object-ui/fields build-> exit 0,find packages/fields/dist -name '*.css' | wc -l= 0. - Published tarball
@object-ui/fields@17.3.0: 274 entries, zero.css. Itspackage.jsonstill declares"./style.css": "./dist/index.css". - Control
@object-ui/components@17.3.0: shipspackage/dist/index.css(165 871 bytes).
2. Premise measurement (#3884's method)
Compiled fields' built
dist/index.js+dist/index.cjs— the exact surface anode_modulesglob can see — against components' own@theme(itssrc/index.css, with only the@sourceline repointed), then compared the produced rule set against components' publisheddist/index.css(downloaded tarball, not a local build).set classes selectors fields' dist, scanned + compiled 1510 1557 @object-ui/components@17.3.0publisheddist/index.css1358 1411 in fields, NOT in components' published sheet 152 150 Method control (the same pipeline pointed at components' own dist, reproducing #3884's reading): 0 classes / 0 selectors missing — 1358 vs 1358, i.e. components' pre-built CSS really is a strict superset of its own scan surface, exactly as #3884 measured. The pipeline is therefore validated, and the 152 are real.
Spot-traced to fields source, so these are not bundling artifacts:
class origin cursor-crosshairpackages/fields/src/widgets/SignatureField.tsx:113group/emailpackages/fields/src/index.tsx:1165text-indigo-700packages/fields/src/index.tsx:922(tag colour map)min-w-[8ch]packages/fields/src/widgets/TagsField.tsx:79hover:fill-yellow-500packages/fields/src/widgets/RatingField.tsx:833. The split that decides the fork
Of the 152, 17 depend on the ObjectUI
@themeblock, which lives inpackages/components/src/index.cssand is not published (filesshipsdistonly). No consumer-side@sourcecan regenerate them — #3780 measured that grid already:bg-background/60,bg-destructive/5,bg-muted/60,bg-primary/20,border-background,border-border/40,border-muted-foreground/25,border-muted-foreground/30,focus-visible:ring-ring/60,focus:ring-ring/60,hover:bg-accent/30,hover:bg-muted/80,hover:bg-primary/10,hover:bg-primary/50,hover:border-primary/50,ring-border/60,ring-destructive/50The remaining 135 are plain Tailwind built-ins (palette, sizing, positioning) and are recoverable by an
@sourceovernode_modules/@object-ui/fields/dist.4. Corroboration: no in-repo host relies on a pre-built fields sheet
Every host that renders fields reaches into fields' source instead —
apps/console/src/index.css:17,examples/console-starter/src/index.css:9,examples/byo-backend-console/src/index.css:29, all@source '.../packages/fields/src/**/*.{ts,tsx}'. That is a workspace-relative path no external consumer can write. So the repo has never exercised the./style.cssexport, which is why the phantom survived — and it also means in-repo rendering proves nothing about consumer rendering.5. Why removal alone is no longer a no-op
The ruling's rationale was "an export that has never resolved cannot have a working consumer, so removing it breaks no one". That still holds for the export. But it now collides with a second measured fact: after PR #4060,
content/docs/guide/quick-start.md:60teachesThat is the whole styling setup: you do not add
@sourcelines for the ObjectUI packages, and pointing Tailwind at them insidenode_modulesonly regenerates utilities the import already gave you.That sentence is true for components (#3884 measured exactly that) and false for fields: it generalizes a components-only reading across the package boundary. A consumer following the current quick-start today gets none of the 152. Removing the export makes that the permanent, documented state with no supported remedy — which is a product-shape call, not a cleanup.
Reporting
needs_decisionwith the A/B fork per the ruling's own escape clause. No files changed, no PR, worktree torn down.
Generated by Claude Code
- Local build:
Delegated ruling #2 (objectui seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3, 2026-08-10): the premise of ruling #1 is falsified by the measurement above — ruled route is now B, narrow variant. Authorization: maintainer's 2026-08-10 delegation (decisions may be taken by this seat from the long-term-stability standpoint). Veto window open.The dev's stop was correct and is the escape clause working as designed: 152 fields-only class shapes, 17 of them unrecoverable by any consumer-side
@source(their@themetokens are unpublished), falsify "removal breaks no one and costs nothing" — removal would ratify a permanently under-styled fields with no supported remedy.Ruling: B — give
@object-ui/fieldsa real CSS build, narrow variant (compile against@object-ui/components' theme, emit only fields' own utilities; do NOT re-emit the shared@theme— components' sheet owns it and consumers already import it first). Three axes:- 实际业务需求 — measured, not asserted: the 152 utilities style nine named shipped widgets (tag colours, signature crosshair, rating hover, tags sizing); fields' whole job is rendering inputs. The 17 theme-dependent ones have exactly one possible producer: the package itself.
- 长远合理性/稳定性 — B is the existing
packages/components/scripts/build-css.mjspattern applied consistently, not a second mechanism; it is the only fork under which the published manifest tells the truth (same shape as the maintainer's fix(plugin-map): 顶层style是内联 CSS,不再被当作 MapLibre style 读 (#5017) #5175 ruling: when the capability and the declaration both exist, enforce — make the implementation catch up — rather than remove). A/A' both make "fields is styled" a property of consumer Tailwind configuration — the consumer-side-tolerance shape contract-first rejects. - 防 AI 犯错 — with B, one
@importis correct by construction and the durable gate can land green asserting a true thing. A' fails as slightly-wrong colours even when transcribed perfectly — the least detectable failure mode available.
Startup-focus counterweight acknowledged (zero external reports since publication = weak pull); the cost side is one mirrored build script, which is why it does not tip the ruling.
Implementation scope (same PR, per the dev's own note):
- fields CSS build (narrow), export stays and becomes true; state in the PR how the coupling to components' unpublished
src/index.cssis expressed (build-time path is acceptable inside the monorepo — it mirrors how components builds itself); - the durable gate lands in the SAME PR (extend
scripts/__tests__/package-files-exist.test.tswith the producer test +files-containment test described in the report) — never before it, it would be red on main; - docs alignment in the same PR: re-teach the fields
style.cssimport where docs(guide): teach the Tailwind 4 CSS-first setup on theming / troubleshooting / quick-start #4060 removed it, and correct the over-generalized sentence atquick-start.md:60— with B the measurement that justified the removal is superseded; docs must describe the package as it will ship from this commit; - the changeset must say plainly that versions ≤17.3.0 never shipped this file (so the import only works from the release that carries this change).
Generated by Claude Code
ACCEPT (objectui seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4076, implementing Delegated Ruling #2 (route B, narrow variant).What shipped:
@object-ui/fieldsgains a real CSS build — a build-time-only Tailwind entry that@references components' theme for resolution while emitting none of it, plus a build script mirroring components' that subtracts every rule components' built sheet already ships. Output 21.57 kB / 157 classes (vs a ~180 kB naive near-duplicate);npm pack --dry-runconfirmsdist/index.cssis in the tarball — the"./style.css"export resolves for the first time in the package's life. Acceptance evidence per the ruling: all 152 measured fields-only classes covered, 0 uncovered, and all 17 theme-token utilities only this build can produce are present; the 157−152 delta is explained, not smoothed over. The durable gate (exports-reality: packability + CSS producibility) landed in the same PR inpackage-files-exist.test.ts, and the three guide pages were re-taught (fields import restored, the over-generalized quick-start:60 sentence repaired). Changeset minor, accepted by the Bump Policy gate.Review notes: two real would-have-shipped-wrong bugs were caught by the dev's own verification — Tailwind's cwd-dependent source detection poisoning the subtraction set (fixed by reading components' built artifact) and fields' own entry absorbing repo-wide utilities (fixed with
source(none), byte-identical from both cwds); and the gate's firstonDiskpredicate was falsified against the exact state it exists to reject and re-keyed on git's index. Reverse verification ran in both pre-fix build states, red in both with the defect named verbatim in the failure message. Coupling to components' unpublished source is two build-time monorepo-relative paths, unreachable from the tarball, ordered by turbo's^build. Region exclusivity vs #3569 held. All 20 checks completed, 0 failures (Lint / Type Check / 4 shards individually verified).Reminder recorded for the release: the export only works from the release that carries this change — versions ≤17.3.0 never shipped the file (stated in the changeset).
Landing next: flipping ready and driving to MERGED.
Generated by Claude Code
Out-of-scope finding from the #3883 / #3884 docs sweep (PR pending). Not fixed there — that PR's file surface is
content/docs/guide/*.mdonly.What was checked, on
origin/main@11c1e71packages/fields/package.jsondeclares the subpath:but nothing in that package appears to produce
dist/index.css:packages/fields/package.jsonscripts.buildistsc && vite build— no CSS step. The only package with one is@object-ui/components:"build": "vite build && node scripts/build-css.mjs", andscripts/build-css.mjsexists only there (grep -rln build-css --include=package.json packages/returns one file).packages/fields/srccontains no.cssfile at all, and no module under it imports one (grep -rn 'import.*\.css' packages/fields/srcis empty). Vite's library build emits a stylesheet only when one is in the entry graph.packages/fields/vite.config.tshas no CSS entry orcssCodeSplithandling.@object-ui/fields/style.cssoccurs only incontent/docs/guide/quick-start.md. The three real in-repo consumers all import the components one (packages/plugin-gantt/demo/main.tsx,packages/plugin-grid/demo/main.tsx,packages/plugin-grid/demo/bulk-actions.tsx).Why it matters
content/docs/guide/quick-start.md:57teaches consumers to write@import "@object-ui/fields/style.css";as step 3 of the first-run setup. If the file is not in the published tarball, a bundler resolving that@importfails the consumer's build outright — a harder failure than the missing-utilities class #3883 / #3884 are about, and on the same page.What I did NOT verify, and how to settle it
I did not run the build: this was a docs-only worktree with no install, and the #3883 dispatch's measurement basis explicitly scoped me to cheap spot-verification. So the evidence above is a source read, not a measurement. The deciding runs are:
pnpm --filter @object-ui/fields build && ls packages/fields/dist/index.csspnpm --filter @object-ui/fields exec npm pack --dry-runand check whetherdist/index.cssis in the file list.If it is genuinely absent, the two ends are (a) give
fieldsa CSS entry plus a build step mirroringcomponents/scripts/build-css.mjs, or (b) drop the./style.cssexport and thequick-start.md:57line that teaches it. Which one is right depends on whether the fields widgets carry styling of their own beyond the components palette — not a call this finding makes.Dedup
Searched open issues for
fields style.css,"@object-ui/fields" css,exports dist/index.css build,subpath export missing file. Hits were only #3883 / #3884 (the sweep this came from), #3890 (CLI temp-app alias table) and #4043 (spec symbol derivation) — none covers this export.Generated by Claude Code