Uh oh!
There was an error while loading. Please reload this page.
fix(types): one authority for ActionSchema and the Breadcrumb pair - #6936
Merged
Merged
Conversation
The first batch of objectui#6349: the three collisions that live entirely inside `packages/types`, where a barrel consumer cannot disambiguate them. `ActionSchema` was declared in `crud.ts` and in `ui-action.ts`. Measured member-by-member the two share 9 keys out of 28 each, `crud.ts` extends BaseSchema and pins `type: 'action'` while `ui-action.ts` extends nothing and types `type` as `ActionType` — unrelated types, so this took the rename branch (objectui#5044 precedent). `ui-action.ts`'s declaration is now spelled `UIActionSchema`, the name `src/index.ts` has always published it under; the barrel alias becomes a plain re-export and the public surface is unchanged. `scripts/check-action-forward-parity.mjs` extracts that interface by name and its own error message asks to be re-pointed when it moves, so it is, together with its fixtures; `check-spec-symbol-derivation.mjs`'s ALLOW reason described both shapes and now describes the one that is left. `BreadcrumbItem` / `BreadcrumbSchema` were declared in `data-display.ts` and in `navigation.ts`. The data-display pair was a strict SUBSET copy — no key declared differently, missing `icon` / `onClick` / `siblings` and `maxItems` — while `registry.ts`, the barrel, the zod mirror, the renderer and the docs page were all already on the navigation declaration. `data-display.ts` re-points at it, so the published `@object-ui/types/data-display` subpath and the `DataDisplaySchema` union finally declare what the renderer honours. The three `KNOWN_COLLISIONS` lines come down in the same change; that baseline fails in both directions, so converging without deleting them is red too. 43 entries -> 40. Refs #6349 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
Contributor
✅ 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
|
os-sam
marked this pull request as ready for review
August 31, 2026 02:59
Uh oh!
There was an error while loading. Please reload this page.
os-sam pushed a commit
that referenced
this pull request
Aug 31, 2026
…inor Two fixes to the review of #6318's PR. 1. objectui#6273 (the 2026-08-25 family ruling objectui#6172 / 甲-A1) went red: the two schemas this branch added to `@object-ui/types` collided by name with the pre-existing declarations in their owning plugins. The two shapes were compared STRUCTURALLY before choosing, not by reading: a tsc probe asserting mutual assignability in both directions, per pair, plus a per-member probe for `language` — the one member the two spelled differently (`'javascript' | … | string`, which TypeScript collapses to exactly `string`). Every relation held, and a deliberate counter-probe against an unrelated shape failed as required, so the green was a measurement rather than a vacuous assertion. Same heritage, same member set, same per-member types and optionality ⇒ these are re-points, not renames. Direction is forced: `@object-ui/types` is the lower layer and cannot import from a plugin without a cycle, so the plugins re-point at types'. `export type { X } from '@object-ui/types'` is a re-export, which the gate does not count as a second declaration. ⛔ `KNOWN_COLLISIONS` is untouched (shrink-only), and so is the gate's own test file — #6936 and #6937 both have unlanded edits to it. The published import paths are unchanged: `check:readme-exports` still resolves both README self-imports through the re-export (386 judged, 0 wrong-path, 0 fabricated), and `objectui check` reports a byte-identical file list, so the accept set of the validator did not move. 2. Regrade the changeset `patch` → `minor` for `@object-ui/types`, per contract review: the package's own precedent (the `visible` widen, and `ViewNavigationConfig`) grades a change of this size `minor` on the stated grounds that it is more than patch describes. Purely additive, so no migration note is owed beyond the regrade. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
This was referenced Aug 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #6349 — first batch only: the three collisions that live entirely inside
packages/types, where a barrel consumer cannot disambiguate them. 43 of the 46 census names remain and #6349 stays their owner, so this isRefs, notFixes.Verified on
c7bc6155b. ⛔ Do not mark ready / enqueue / auto-merge — draft by dispatch order.The batch, and the delta on the merge-base
The census table was measured on
79ebf30d1. Re-derived on my merge-base40c479af2with the gate's own matcher (scripts/js-comment-mask.mjs+ the same three regexes, same population bound): 43 colliding names, not 46 —FormFieldSpec/FormSectionSpec/FormViewSpecleft the tree with the spec-bridge retirement (#6366) andKNOWN_COLLISIONSalready records that in a comment. All three of my names still collided, at the same sites the table names, so none had to be dropped:After: 40 collisions, 40 baseline entries. Population floors intact — 38 published packages, 1,384 files, 2,201 authority sites (was 2,204: three declarations removed, one barrel alias became a plain re-export).
Shapes compared structurally before choosing — the method
Not by reading. A TypeScript-AST probe over both declarations of each name, comparing heritage clause, member set, and each shared member's declared type and optionality:
ActionSchemacrud.ts(28 members,extends BaseSchema)ui-action.ts(28 members, no heritage)type:'action'vsActionType;variant;methodBreadcrumbItemdata-display.ts(2)navigation.ts(5)icon,onClick,siblingsBreadcrumbSchemadata-display.ts(3,extends BaseSchema)navigation.ts(4, same)maxItemsThat split is what picks the remedy per name, and the two remedies are different.
ActionSchema→ rename, because the two are different things9 shared keys out of 28 each; one is a UI node in a component tree (
type: 'action'), the other a spec-v2 action definition (name,locations,params,target). Re-pointing either at the other would silently hand a consumer a different type — exactly the harm the ruling is about. #5044 is the precedent for choosing the surviving name, and here the tiebreak is already settled by the barrel:src/index.tspublishescrud.ts's asActionSchemaandui-action.ts's asUIActionSchema(export type { ActionSchema as UIActionSchema }, line 1037). Soui-action.ts's declaration is renamed to the name the package has always published it under, and the alias becomes a plain re-export.packages/components' own CHANGELOG already calls that typeUIActionSchemain prose; only the declaration was out of step.Consumers. Nothing outside
ui-action.tsimported the old spelling:exportsinpackages/types/package.jsonhas no./ui-actionsubpath, and the only three files importing that module by path takeActionParamand the value exports, neverActionSchema. EveryUIActionSchemauser in the repo goes through the barrel and is untouched.BreadcrumbItem/BreadcrumbSchema→ re-point, because one copy was staleNot a second dialect — a subset with nothing declared differently. Everything that actually reads a breadcrumb was already on
navigation.ts:registry.tsmaps the'breadcrumb'component type to it,src/index.tsre-exports it under the bare names,zod/navigation.zod.tsmirrors it (icon,onClick,siblings,maxItemsall present), theui:breadcrumbrenderer consumes it and says so in a comment, andcontent/docs/components/data-display/breadcrumb.mdxdocumentsiconandmaxItems.data-display.tsnow re-exports the one authority.Consumers, and one that imported the losing file directly. Yes —
packages/types/src/__tests__/zod-mirror-parity.test.ts:138importsBreadcrumbItem/BreadcrumbSchemafrom../data-displayand pairs them against thenavigation.zod.tsmirrors (lines 610–611). It was checking the navigation Zod schema against the data-display TS interface: a live instance of the defect, silent because the mirror is the wider side. After the re-point those pairs compare the mirror against the declaration it actually mirrors; the file is green either way, but it is honest now. No ledger entry (KnownDrift/UnmirroredDeclared/RuntimeOnlyDeclared) names either pair, so nothing went stale.Emitted
.d.ts— what changed shape, and for whomBuilt
@object-ui/typeson both sides (a second worktree at40c479af2, since removed) and diffed the emitted declarations:dist/index.d.tsActionSchema as UIActionSchema→UIActionSchema. No exported name added, removed or reshaped.dist/crud.d.ts,dist/navigation.d.ts,dist/registry.d.tsdist/ui-action.d.tsActionSchema→UIActionSchema; members unchanged. Not reachable — no./ui-actionsubpath.dist/data-display.d.tsinterface Breadcrumb*declarations replaced byexport type { BreadcrumbItem, BreadcrumbSchema } from './navigation.js'dist/zod/*.zod.d.ts— 7 files (data-display, form, index, layout, navigation, objectql, views)dist/data-display.d.tsas a hot control that FAILS that same test, as a real change must; and two clean builds of the same head commit are byte-identical, sotscis deterministic here and the reordering is attributable to this PR's module-graph change. The./zodsubpath's declared types are unchanged in meaning.The first four rows are the complete shape statement; the zod row is what makes it complete as a byte statement too — my first enumeration listed only the shape changes and was incomplete on bytes.
So exactly one audience sees a member change shape: a consumer of the published
@object-ui/types/data-displaysubpath, or of theDataDisplaySchemaunion. TheirBreadcrumbItemgainsicon/onClick/siblingsand theirBreadcrumbSchemagainsmaxItems. All four gained members are optional and nothing narrows, so every value that type-checked before still does, in both directions — and what the subpath declares is now what the renderer already honoured and the docs already described. Gradedminorper the version-alignment rule (direct precedent: #6574 addedbindtoBaseSchema, the same class, gradedminor).keyof, so a consumer that EXHAUSTS it does break. Measured, direction predicted first:Record<keyof BreadcrumbItem, string>written with the two old keys compiles on base and fails on head —TS2739: ... missing the following properties ...: icon, onClick, siblings. In-repo consumers of that shape: zero (controlled grep forkeyofover both names acrosspackages/andapps/), and there is no in-repo,examples/orskills/importer of the@object-ui/types/data-displaysubpath at all. So this is a documented caveat for downstream, not a blocker — but it is a real class, and the landing record should not deny it. The changeset already carries the value-scoped wording.The baseline gate shown non-vacuous
The instrument is the two-way failure itself, so it is demonstrated rather than supplemented. Direction predicted in writing first: deleting a
KNOWN_COLLISIONSline while the name still collides leaves the observed collision with no baseline entry, soreconcileputs it infreshwith the texta NEW colliding name— thefreshassertion fails,stalestays green.Run on the clean merge-base tree, mutating the fact (the baseline line for
BreadcrumbItem), never the assertion, undertrap … EXIT INT TERMwith absolute paths fromgit rev-parse --show-toplevel:Observed direction matched the prediction. Restore proved both ways with
git checkout HEAD -- <abs path>(never bare):git diff HEADempty and disk hash back to542a4459…,git statusclean.Gates
Exit codes captured before any pipe; every verdict below is the line the gate printed for itself.
pnpm --filter @object-ui/types type-check(hyphenated;tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json, so test files are covered)c7bc6155b: 82 / 1021.pnpm exec eslint .(plain form, no--no-inline-config)--format json, 0 errors, 11,516 pre-existing warnings. All seven changed files are in the population; every warning on them is@typescript-eslint/no-explicit-anyon lines outside my hunks.node scripts/check-changeset-presence.mjscheck:action-forward-parity5 surfaces checked against 39 runtime-read keys from 4 consumerscheck:spec-symbols16 declared dialects, 0 untriaged collisions in 0 packagescheck:control-bytescheck:doc-types,check:designer-field-key-parity,check:icon-record-names,check:self-import,check:esm-specifierscheck:readme-exportsthe population COLLAPSED -- this run proves nothingwithout a full workspace build (388 complaints, alldist/index.d.ts is not on disk, across packages this diff never touches).readme-exports.ymlbuilds first; this one is CI's.Two touches outside
packages/types, both declaredNeither is drive-by; both are the same defect and both are named here because the divide-line scan belongs in the PR body.
scripts/check-action-forward-parity.mjsextractsinterface ActionSchemafrompackages/types/src/ui-action.tsby name, and its own failure message reads "The renderer view moved or was renamed; re-point this gate at it." — so it is re-pointed atUIActionSchema, together with the three fixtures inscripts/__tests__/check-action-forward-parity.test.tsthat spell the old name (fixture triage: they merely used it; the negative controlinterface Otherstill proves a renamed view is red). That suite is owed because the gate script itself is edited — and it earned its keep: it went red first, 32 failures, before the fixtures were corrected.scripts/check-spec-symbol-derivation.mjs'sALLOW["@object-ui/types:ActionSchema"]reason said "Two deliberate objectui-side shapes". After the rename onlycrud.tscarries the name, so the prose is corrected; that file's own header says such a comment is load-bearing rather than decorative. The entry stays (it is keyedpackage:nameandcrud.tsstill matches), so the shrink-only ratchet does not fire.Scope
Exactly the three names the PM sized. Nothing else from the 46 — not while in the file.
KanbanCard/KanbanColumn(#6155),KanbanSchema/MarkdownSchema(#6172) andComponentConfig(#6298) are untouched. The #6298 fence held:git diff --name-only -- packages/types/src/base.tsis empty; the eight changed files are disjoint frombase.ts.Amendment record
This body was amended once after the contract review (#6349 (comment), verdict ACCEPT-WITH-CONDITIONS): the compatibility sentence is now value-scoped with the
keyofcaveat named, and the.d.tstable gained the sevenzod/*.zod.d.tsbyte-differences the reviewer measured as order-only. Body only — no commit; the head is stillc7bc6155b, the tree the gates above were run on. Authored by Claude Code, sessionhttps://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB.Generated by Claude Code