Uh oh!
There was an error while loading. Please reload this page.
fix(plugins,app-shell): resolve the six spec-named symbol collisions with 17.0.0 GA - #4660
Merged
Conversation
…with 17.0.0 GA `@objectstack/spec@17.0.0` exports six names objectui declares locally, so `check-spec-symbol-derivation` exits 1 on a GA-installed tree. Resolve all six per the #4043 doctrine, keeping current main (rc.6) green. Four `Object*Props` and their barrel re-exports are DIFFERENT things sharing a name: the spec's are the authored props documents of the `object-*` elements (`z.input<typeof Object*PropsSchema>`), these are the renderers' props (live `dataSource`, pre-fetched records, host callbacks). Renamed to `Object*ComponentProps`, following `PageHeaderProps` -> `PageHeaderComponentProps` and the `Record*ComponentProps` family; every old name stays exported as a deprecated alias so no importer breaks. `SECRET_MASK` IS the spec's constant (ADR-0100, objectstack#7572), so the doctrine's preferred arm is to import it — which cannot be written while this repo is pinned to `^17.0.0-rc.6`. Renamed to `OBJECTUI_SECRET_MASK` at its declaration, with the one-line burn-down recorded there. It is package-internal (not re-exported from the app-shell barrel), so nothing published changes. Tripwires per package pin that the new names stay free and that each deprecated alias still denotes the same type. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RnQd8iMMUwXQEV1crFmQiQ
…50 tripwires
A namespace import of `@objectstack/spec/ui` necessarily binds `FormField` and
`FormFieldSchema`, which eslint.config.js restricts to an ERROR (objectui#3090):
those spec form-view types erase to `any`, so importing them silently deletes
type safety. A type-level `import('@objectstack/spec/ui').X` names one member
and declares no binding, so the tripwire asks its question without opening that
door. Reason recorded in each file so it is not "cleaned up" back.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RnQd8iMMUwXQEV1crFmQiQThe latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
marked this pull request as ready for review
August 14, 2026 16:34
Uh oh!
There was an error while loading. Please reload this page.
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.
Fixes#4650
Pre-bump adaptation 2 of 2 for #4636 / PR #4639. Resolves all six spec-named symbol
collisions
@objectstack/spec@17.0.0introduces, socheck-spec-symbol-derivationis greenon a GA-installed tree — while staying green on current
main(pinned^17.0.0-rc.6). Nodependency change here; that is PR #4639's job.
The authoritative six
The card named five and left the sixth to be derived. Measured by running the real gate
against a GA-installed tree (method below), the six are:
SECRET_MASKpackages/app-shell/src/views/metadata-admin/widgets.tsx:1852@objectstack/spec/dataObjectCalendarPropspackages/plugin-calendar/src/ObjectCalendar.tsx:64@objectstack/spec/uiObjectCalendarPropspackages/plugin-calendar/src/index.tsx:21@objectstack/spec/uiObjectFormPropspackages/plugin-form/src/ObjectForm.tsx:48@objectstack/spec/uiObjectGridPropspackages/plugin-grid/src/ObjectGrid.tsx:351@objectstack/spec/uiObjectKanbanPropspackages/plugin-kanban/src/ObjectKanban.tsx:106@objectstack/spec/uiThe unnamed sixth is #3 — the plugin-calendar barrel's
export type { ObjectCalendarProps };.It is a separate finding rather than a duplicate of #2 because it carries no
fromclause:the gate skips a re-export whose module specifier is relative or
@object-ui/*(whatever itpoints at is judged at its own declaration), but with no specifier there is nothing to
recognise, so the barrel line is judged as its own declaration. Spelling the alias
export type { X as Y } from './ObjectCalendar'is what collapses it back to one judgement.Per-symbol disposition
The four
Object*Props— different things sharing a name ⇒ rename + deprecated aliasThe spec's are authored props documents of the
object-*elements —z.input< typeof ObjectGridPropsSchema >, serialisable authoring keys (label,fields,defaultFilters,pagination, …). Ours are the renderers' props: a livedataSource,records pre-fetched by a parent, and the host callbacks. Two layers under one word, so the
local ones are renamed, not derived.
The suffix is not invented — it is the split this repo already made twice for exactly this
shape:
PageHeaderProps->PageHeaderComponentProps(app-shell, in that package's renametable) and the
Record*ComponentPropsfamily in@object-ui/types, whose spec counterpartsare
RecordActivityProps/RecordDetailsProps/ ….@object-ui/plugin-calendarObjectCalendarComponentProps@deprecatedbarrel alias@object-ui/plugin-formObjectFormComponentProps@deprecatedbarrel alias@object-ui/plugin-gridObjectGridComponentProps@deprecatedbarrel alias@object-ui/plugin-kanbanObjectKanbanComponentProps@deprecatedbarrel aliasEach alias is
export type { New as Old } from './Module';— thefromclause isload-bearing, not cosmetic: without it the gate judges the alias as a fresh declaration
(finding #3 above is precisely that shape).
SECRET_MASK— the SAME thing ⇒ rename now, derive at the bumpThis one is genuinely the spec's constant: identical value, and 17.0.0 moved the ADR-0100
credential read mask into
@objectstack/spec/data(objectstack#7572) so its two readers stopeach declaring a byte-identical literal. The doctrine's preferred arm is therefore to
import it — and that import cannot be written on this branch, because
^17.0.0-rc.6doesnot export the name. The two clauses collide, and "must land green on current main" wins.
So the copy stays under a name that cannot be read as canonical,
OBJECTUI_SECRET_MASK, withthe whole burn-down recorded as one line at the declaration: when #4639 lands, replace the
literal with a re-export from
@objectstack/spec/data.Nothing published changes. The constant is not part of
@object-ui/app-shell's API:src/index.tsre-exports a named list from./views/metadata-admin, that barrel does notre-export
widgetsat all, there is noexport *on either path, and the package publishesonly
.. Its only readers arewidgets.tsxitself and the siblingSecretWidget.test.tsx.No alias is owed, so none is added — a
SECRET_MASKalias would keep a spec-named symbol inthe tree for zero consumers.
Evidence — the gate, both directions, at the pushed HEAD
Both readings taken at
66adc24fa(working tree clean).Current
main's pin (17.0.0-rc.6), before and after: green either way — the collisionsare GA-introduced, so this change is invisible to today's farm except through the types and
tests it adjusts.
GA tree (
@objectstack/spec@17.0.0) — the reading this card exists for. Onorigin/mainit is exit 1 with the six above; at this HEAD:
Note both readings keep 13 ALLOW entries matched and the same 3
@object-ui/typesledgerentries live: no existing exemption went stale in either direction, which the gate's own
ratchets would have failed on.
Method for the GA reading (stated because it is not the obvious one): rather than a
second full install of PR #4639's branch,
npm pack @objectstack/spec@17.0.0was unpackedinto this worktree's
node_modules/.ga-measure/and the@objectstack/specsymlinkrepointed at it, so the real gate script ran unmodified over this branch's sources with GA's
export set; the rc.6 link was then restored. Placing the copy inside the worktree matters —
zodand the rest resolve by walking up to the workspace root, so the spec's.d.tsfilestype-check properly instead of degrading to
anyand under-reporting names. The name countsabove (4834 vs 4912) are the check that the two readings really are different export sets.
Other gates at
66adc24facheck:spec-symbols(rc.6 pin)check:spec-symbols(GA overlay)check:phantom-depscheck:control-bytescheck:action-forward-paritycheck:i18n-keys/check:i18n-driftcheck:skills-pathscheck-changeset-presence/-fixed/-no-majoreslint --quietover the changed filestype-check— the four plugins + app-shellvitest runover the touched packagesvitest runover the new + touched test files at this HEADcheck:phantom-depsis the one worth calling out: the new tests import@objectstack/specfrom two packages that do not declare it, which is legal only because they are TOOLING files
served by the workspace root's
devDependencies. The gate confirms it rather than my readingof it — "2162 tooling import(s) are declared only by the workspace root". No
package.jsonand no lockfile entry changes here, deliberately: adding a spec devDep to plugin-calendar and
plugin-kanban at the rc range would be a fifth and sixth pin for PR #4639 to find and raise.
Tripwires, and proof they are load-bearing
The gate's own remedy text asks a rename to come with "a tripwire test asserting the spec does
not own the name". Each of the four plugins gets
__tests__/spec-symbol-4650.test.tspinning,at compile time, that the spec does not own the NEW name and that the deprecated alias still
denotes the SAME type; app-shell's existing
spec-symbol-parity.test.tsgains theSECRET_MASKentry in a newRENAMES_PENDING_GAtable, whose "the spec still owns the oldname" half arms itself when the installed spec stops being a pre-release.
Compile-time assertions are erased before vitest runs, so they were verified by ablation
rather than assumed — direction predicted before running, both times red:
error TS2578: Unused '@ts-expect-error' directive.Equal< ObjectGridProps, string >⇒error TS2344: Type 'false' does not satisfy the constraint 'true'.Both restored from the commit and re-confirmed green (
git statusclean, i.e. byte-identical).One implementation note recorded in each test file, because the natural spelling is banned:
the spec is reached through a type-level
import('@objectstack/spec/ui').Xrather thanimport type * as SpecUi, because a namespace import of that subpath necessarily bindsFormField/FormFieldSchema, whicheslint.config.jsrestricts to an error(objectui#3090 — those spec types erase to
any, so importing them silently deletes typesafety). This cost a lap; the reason is written down so it is not "cleaned up" back.
Scope
No file outside this card's surface.
packages/app-shell/src/views/ObjectView.tsxandplugin-list / plugin-view (#4649) and the disclosure types/components (#4632) are untouched.
The
striped/bordered/virtualScrollretirement and the new GA authoring surfacesfrom PR #4639's list are not addressed here — they are separate adaptations.
Generated by Claude Code