Uh oh!
There was an error while loading. Please reload this page.
fix(plugin-dashboard): converge the two private reference-bearing field tables onto core's published family (#5692) - #5888
Merged
Conversation
…ld tables onto core's published family (#5692) `packages/plugin-dashboard` held two hand-maintained copies of the reference-bearing field family — `LOOKUP_TYPES` in `recordFields.tsx` and an inline disjunction inside `computeLookupExpand` in `ObjectDataTable.tsx`. Neither derived from nor pinned against `@object-ui/core`'s published `EXPANDABLE_FIELD_TYPES`, and both predate objectui#5312, whose docblock recorded `paramToField` as the LAST private copy of the rule. Both now read core's object and carry the identity pin the earlier conversions carry, so a member-identical private copy fails rather than quietly re-forking the table. The copies were not in a subset relation with the shared family in either direction, so converging changes behaviour twice: - `tree` is GAINED — a self-referencing hierarchy column is now `$expand`-ed on the dashboard road, the treatment the form and grid roads already gave it. - `reference` is DROPPED, measured rather than preferred: the spelling is absent from `@objectstack/spec`'s closed `FieldType` vocabulary and refused by `FieldSchema.safeParse` — probed with `lookup` / `master_detail` / `user` / `tree` as live controls and the retired `owner` plus a nonsense spelling as dead ones — so no spec-compliant object schema can declare a field whose stored type is `reference`. The measurement is carried forward as an executable pin, so the day the spec adds the spelling this goes red and the membership question reopens. `EXPANDABLE_FIELD_TYPES` itself is unchanged: the measurement did not license widening a published shared set. Core's docblock ledger is corrected — its hand-kept "four consumers" count was already stale by two, and objectui#5312's "LAST private copy" claim was false by two more. objectui#4914's retirement gate is untouched and still runs ahead of both predicates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuPCi56cnGyykygi3z9w4m
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-zhuang
marked this pull request as ready for review
August 23, 2026 17:42
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#5692
packages/plugin-dashboardheld two hand-maintained copies of the reference-bearing field family —LOOKUP_TYPESinrecordFields.tsxand an inline disjunction insidecomputeLookupExpandinObjectDataTable.tsx. Neither derived from nor pinned against@object-ui/core's publishedEXPANDABLE_FIELD_TYPES. Both now read core's object and carry the identity pin the earlier conversions carry (#4770 / #4790 / #4815 / #5312).Convergence is ruled (PM,
domain:uiseat, dispatching this card). The membership question the card left open was decided by measurement, not by preference — also the PM's instruction, and the measurement is below with its controls.The membership measurement: is
referencea live field type, or a dead spelling?The card noted the sets are not in a subset relation either way, so convergence moves behaviour in two directions. The
referencedirection had a prior factual question, and the probe ran subject and controls on the same read, so a probe that had lost hold of the vocabulary fails as a broken probe rather than reporting the subject absent.Leg A — the spec's closed
FieldTypevocabulary.Leg B — the real parse-time producer gate,FieldSchema.safeParse.Four positive controls held, two negative controls held, and both legs agree.
referenceis a dead alias spelling, not a live stored field type. It sits at exactly the levelownersat at before #4814 retired it, and at the level of pure nonsense.The card's triage comment asked whether the
PARAM_TYPE_ALIASESfold seen on a sibling surface (#5654) holds here. It does not fold on this road — the dashboard predicates readobjectSchema.fields[…].typeraw — but that strengthens rather than weakens the reading: the one surface where the spelling is live at all declares it a legacy dialect alias and folds it tolookupbefore any field-type data is read (PARAM_TYPE_ALIASESinpackages/app-shell/src/utils/paramToField.ts). On the dashboard road the spelling was simply answered for, un-aliased and unreachable.So
EXPANDABLE_FIELD_TYPESis unchangedThe "two independent consumers reached for it" argument does not survive the measurement: they reached for a spelling no producer can emit. Per the PM's fence, a member is not added to a published shared set on plausibility — so
packages/core/src/utils/expand-fields.tsgains prose only, not a member. ItsSetliteral is byte-identical.The two behaviour directions, stated plainly
treecolumn on the dashboard table$expand-ed$expand-edreferencecolumn on the dashboard table$expand-ed$expand-edlookup/master_detail/user$expand-edtreegains expansion — accepted.EXPANDABLE_FIELD_TYPESis the published authority andtreeis a member; a self-referencing hierarchy column being$expand-ed on the dashboard road is the same treatment the form/grid road already gives it. Its cell renders the parent record's display name instead of a bare id.referenceloses expansion — a no-op on real data, on the measurement above. Nothing spec-compliant can produce such a column.Where the behaviour actually lands. Only
computeLookupExpandis on a live path (ObjectDataTable.tsx:338, every dashboard table fetch).isLookupTypeis exported fromrecordFields.tsxbut has no production consumer inpackages/orapps/, and is not re-exported from the package entry — so its convergence is a contract change, not a behaviour change. Stated rather than implied, because a PR claiming two behaviour directions across both faces would be overclaiming. Filed separately as #5876.Verification
All legs on the final commit
91c3f6b3f, tree clean (git status --porcelain= 0 lines). Heavy legs throughscripts/pm/os-verify-lock.sh; exit codes captured before any pipe, and each gate quoted by its own printed verdict line.Tests
Type-check (dependency closures built first)
Scope: 2 of 47with both script names echoed — not a zero-match silent green.Gates
check-changeset-presence.mjs✅ 5 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)check-changeset-no-major.mjs✅ No changeset declares a major bump.check-changeset-fixed.mjs✅ All workspace packages are in the changeset fixed group.check-control-bytes.mjs✅ check-control-bytes: OK (scanned 4890 tracked text file(s); skipped 85 binary).check-phantom-dependencies.mjs✅ Every in-scope import is declared by the package that publishes it.check:spec-symbols✅ spec symbol derivation: 1300 files scanned against 4959 spec export namescheck:self-import✅ No package names itself inside its own src/.check:esm-specifiersSpecifier leg: no un-ledgered package emits an extensionless relative specifier.@object-ui/corewas already a declared dependency of@object-ui/plugin-dashboard, so the new import declares nothing new —check-phantom-dependenciesconfirms it.Lint — narrowed, and the narrowing declared
Ran the two touched packages' own lint rather than the repo-scale
pnpm lint(a CI-owned run):0 errors. The warning populations are the repo's standing
@typescript-eslint/no-explicit-anybacklog on lines this diff does not touch; the diff adds noanyand no new export.The narrowing is sound rather than merely convenient:
eslint.config.js'slanguageOptionssets onlyecmaVersionandglobals— noproject/projectService/parserOptions.project, so type-aware linting is not enabled and this diff cannot move the verdict on any file it does not touch.check:eager-closurewas not run: it readsapps/console/dist/eager-closure.json, emitted during avite buildof the console, and refuses as a broken gauge when absent. That build is CI's. Structurally the delta is nil anyway — the only new cross-package import is@object-ui/coreintorecordFields.tsx, andObjectDataTable.tsx, the file that importsrecordFields.tsx, already imported@object-ui/corebefore this change.Reverse verification — four legs, all discriminating
Every leg carried a
trap … EXIT INT TERMrestore, and each mutation was confirmed on disk before its reading was accepted (removed text at 0 occurrences and injected text present — an editor's exit code proves neither).No rebuild leg is owed, and here is why: the root
vitest.config.mtsaliases'@object-ui/core' → packages/core/src, and the tests import the subjects by relative path inside the same package'ssrc. Nodist/is on the pins' resolution path, so a stale build cannot fake either colour. The green run's identity pin passing is itself proof that test and subject hold the sameSetinstance.recordFields' original private copytreepin, itsreferencepin, andlookupTypes.retiredFieldType.test.ts'sLIVE_RELATIONScontrol. 14 passed.new Set(['lookup','master_detail','tree','user'])ObjectDataTable's original inline disjunctiontreeexpand pin, itsreferencepin. 15 passed.git checkout <branch> -- <both files>git diff HEAD --statempty, porcelain 0, all three mutation markers absent from disk, 18/18 greenLegs 1b and 2b are the point: every membership assertion in the suite is satisfied by a private set holding the same four strings, so a value check would pass on the defect. Only the spy on
EXPANDABLE_FIELD_TYPES.hascatches it.Legs that do NOT discriminate, named as required:
lookupTypes.retiredFieldType.test.tsstayed fully green. It cannot detect a member-identical fork, by construction — which is exactly the gap the new identity pin exists to close, not a defect in that file.computeLookupExpand's pins green and legs 2/2b leaveisLookupType's pins green. That per-face isolation is why these are two pins rather than one.lookup/master_detail/user, both column modes) stayed green in all four legs — which is what makes them controls: the convergence did not take the whitelist with it.Scope
#4914's landed retirement gate is untouched and still runs ahead of both predicates.EXPANDABLE_FIELD_TYPES'Setliteral is unchanged. No other package's copy was re-homed.The card also asked that #5312's false "LAST private copy" claim be corrected. It is corrected in the family's canonical home,
packages/core/src/utils/expand-fields.ts, whose hand-kept "four consumers" count was itself already stale by two. The sentence also stands verbatim in twoapp-shellfiles, which are outside this card's fence — filed as #5875 rather than reached into.Out-of-scope findings (filed unassigned,
finding+domain:ui, nopm:queue, after a duplicate search)user/treeand each carrying the undeclarablereference#5874 — four more private copies of this family (plugin-kanban,plugin-detail×2,resolveActionParams), each missinguser/treeand each carrying the undeclarablereference. Excludes finding(app-shell):ActionParamDialog'sisLookupParamrestates the picker family over RAW param spellings, so a degradedmaster_detailparam loses the #3405 affordances #5654's face.paramToField's "fourth and last private copy" claim is still stated in source and test prose after #5692 falsified it #5875 — the "fourth and last private copy" claim still stated inapp-shellsource and test prose.isLookupTypeis exported fromrecordFields.tsxwith no production consumer — the live$expanddecision iscomputeLookupExpand's #5876 —isLookupTypeexported with no production consumer.Generated by Claude Code