fix(types,plugin-detail): stop publishing the retired owner field type (#4914) - #4932
Merged
Merged
Conversation
…ype (#4914) The three published contract twins in `@object-ui/types` drop `'owner'` — the runtime `ReportFieldSchema.type` enum plus its TS twins `ReportField['type']` and `UserFieldMetadata['type']` — so the package stops answering "legal" for a spelling `@object-ui/fields` refuses with a tombstone (#4814, ruling A'). `InlineFieldInput` disposes consistently with that tombstone: a retired spelling renders the same visible refusal the record form gives, instead of a working person picker the form contradicts. Part of #4914 (items 1-3 + 5). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DbRmJD3iPhXjKr6vhd4Qkv
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
os-steve
marked this pull request as ready for review
August 17, 2026 06:33
os-steve
pushed a commit
that referenced
this pull request
Aug 17, 2026
…n refuses it PR #4932 merged after this branch's base and shrank `owner` out of the published field-type unions in `@object-ui/types`, so the four `type: 'owner'` field literals in the new pin no longer compile (TS2322) — the contract twin working exactly as designed. The suite still has to author the refused spelling, because what it pins is the RUNTIME disposition for a field that is ALREADY STORED that way, which no compile-time union can reach. Confine the escape to one helper (`retiredFieldLiteral`) so strictness holds everywhere else in the file, and state there that the refusal is the point rather than an obstacle. Verified the escape is load-bearing, not decorative: removing `as never` reproduces TS2322 at all four call sites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DbRmJD3iPhXjKr6vhd4Qkv
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Aug 17, 2026
…eld types (objectstack-ai#4942) * fix(fields): close the FieldEditWidget delegation road for retired field types (objectstack-ai#4931) `hasFieldEditWidget` answered `resolveInlineEditType(type) in EDIT_WIDGETS`, and `resolveInlineEditType` returns a type unchanged when it is already a key of `EDIT_WIDGETS`. While `owner: UserField` sat in that map the alias table — where objectui#4814's retirement lives — was never consulted, so the retirement never applied: `hasFieldEditWidget('owner')` was `true` and the grid's inline cell editor still rendered a working person picker for a field the record form refuses with a tombstone. Gate on the live `RETIRED_FIELD_TYPES` table so the class closes, not the spelling: `hasFieldEditWidget` -> false, `isInlineExcludedFieldType` -> true, and `FieldEditWidget` renders no control while writing the once-per-spelling prescription. Drop `owner` from `EDIT_WIDGETS` and from the cosmetic `COMPACT_EDIT_TYPES` set (objectstack-ai#4914 item 11). The exclusion half is load-bearing: `ObjectGrid` forces `editable: false` on a column exactly when `isFieldInlineEditable` is false, so without it the column stays editable and DataTable falls back to a plain text input that saves a bare string over a stored person reference. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DbRmJD3iPhXjKr6vhd4Qkv * test(fields): escape the retired spelling now that the published union refuses it PR objectstack-ai#4932 merged after this branch's base and shrank `owner` out of the published field-type unions in `@object-ui/types`, so the four `type: 'owner'` field literals in the new pin no longer compile (TS2322) — the contract twin working exactly as designed. The suite still has to author the refused spelling, because what it pins is the RUNTIME disposition for a field that is ALREADY STORED that way, which no compile-time union can reach. Confine the escape to one helper (`retiredFieldLiteral`) so strictness holds everywhere else in the file, and state there that the refusal is the point rather than an obstacle. Verified the escape is load-bearing, not decorative: removing `as never` reproduces TS2322 at all four call sites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DbRmJD3iPhXjKr6vhd4Qkv --------- Co-authored-by: Claude <noreply@anthropic.com>
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 free
to 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.
Part of #4914 — the dispatched fast-follow scope only: items 1-3 (the published contract twins) + item 5 (
InlineFieldInput). Items 4 and 6-15 stay recorded on that card, so this must not close it.Follows #4814 / PR #4915 (merged 05:38Z), which retired the
ownerfield-type spelling under ruling A′ and gave it a loud tombstone. That PR shrank the three public doc unions; this one shrinks their code twins, so the types package and the renderer finally agree.Premise check (all four faces re-verified on this branch, post-#4915)
All four measured faces were present exactly as reported. Nothing was already gone, nothing had moved.
packages/types/src/zod/reports.zod.ts:49type: 'owner'packages/types/src/reports.ts:105ReportField['type']still offered itpackages/types/src/field-types.ts:584UserFieldMetadata.typewas'user' | 'owner'packages/plugin-detail/src/InlineFieldInput.tsx:107,290isUserRefbranch both live#2231 / #2890 overlap check (owed by the claim): no collision. Both cards scope
packages/types/src/zod/objectql.zod.ts(theListViewSchemalegacy-vocabulary phases) andzod/views.zod.ts(DetailViewSchema). Neither namesreports.zod.ts,reports.tsorfield-types.ts. The#2890phase list (fields→columns,densityMode→rowHeight, theshow*family,filters→filter,objectName→data.provider) touches no line this PR edits.What changed
@object-ui/types— the three twins shrink. The Zod enum is the half that mattered most: it is a published runtime validator, so the contradiction was executable, not merely advisory — a report document authored withtype: 'owner'validated green and then rendered a refusal. The two TS unions were the.d.tsautocomplete half, i.e. the surface an AI author copies from. Each carries a retirement comment naming the card and its twin, in the style PR #4915 used onbulkParamToField/paramToField.@object-ui/plugin-detail— inline edit joins the tombstone. A retired spelling now renders the sameRetiredFieldTombstonethe record form does, read live from@object-ui/fields'RETIRED_FIELD_TYPES(never copied), reported once per spelling rather than once per row. Placed after the widget-hint branch, mirroring the form's own precedence.The finding that changed the shape of this fix
The dispatch framed item 5 as "remove the two members, then make sure the removal is not silent". Measured, removing the two members alone changes nothing an author could see:
packages/fields/src/FieldEditWidget.tsx:85still mapsowner: UserFieldinEDIT_WIDGETS, andresolveInlineEditTypereturns a type unchanged when it is already a key — so the alias table is never consulted and the retirement never applies. With the routing member deleted, the type simply reaches the same person picker down the delegation road instead of the routing road.Verified directly during reverse verification (refusal branch ablated, both deletions in place):
tombstone: false,plainTextInput: false, one element withrole=buttonreadingSelect…. So the refusal branch is the only behavior-changing half; the two deletions are hygiene that moves in lockstep with it. Both source comments say this, because the naive reading is wrong in a way that would mislead the next editor.EDIT_WIDGETS:85is not one of the faces #4914 enumerates (that card listsFieldEditWidget.tsx:170, the cosmeticCOMPACT_EDIT_TYPESset). Filed as #4931, a sub-issue of #4914 — out of this dispatch's file surface, untouched here.InlineFieldInputitself is fully protected regardless, since the refusal runs before both roads; the exposed consumer is the grid's inline cell editor.Tests
packages/types/src/__tests__/owner-retired-contract-twins.test.ts— the Zod half pinned by refusal envelope (exactly one issue,typepath,invalid_value, offered vocabulary lacksownerand still hasuser), not a baresuccess === false; the survivor idiom{ type: 'user', name: 'owner' }pinned by a full green parse; a sibling-vocabulary check so the shrink cannot over-reach. The two TS halves pinned with@ts-expect-error, real enforcement here becausepackages/types/tsconfig.test.jsonis chained fromtype-check.packages/plugin-detail/src/__tests__/InlineFieldInput.retiredFieldType.test.tsx— the refusal envelope (testid,role=alert,data-retired-field-type, the prescription text), both wrong outcomes pinned (a working picker and a quiet text box), once-per-spelling console dedupe, an expanded-value case,userunaffected, an unknown type left on its pre-existing quiet fallback, and a structural check quantified over the wholeRETIRED_FIELD_TYPEStable so the next retirement is covered the day it lands.inlineEditTypeCoverage.test.tsx— comment refreshed; it explicitly said the routing set "still listsowner… not forced away here", which this PR makes false.One deliberate non-assertion, stated because it looks like an omission: the test does not pin
hasFieldEditWidget('owner') === truein either direction. That asymmetry belongs to #4931, and a pin on today's value would go red on the very fix that card proposes.Verification, at final commit
ddbf4d92eRepo-root path-filter vitest; heavy runs under
flock -E 99 -w 540 /tmp/os-heavy-verify.lock; dependency closures built before any type-check verdict.Downstream consumer sweep — this is a contract narrowing, so the dependents direction is the one that matters:
pnpm --filter '...@object-ui/types' buildthentype-check— all green, coveringplugin-report(the realReportFieldconsumer),plugin-view,plugin-list,plugin-designer,app-shell,apps/console,apps/site,examples/schema-catalog,examples/console-starterand the rest. No consumer was writingtype: 'owner'; a repo-wide sweep for report fixtures spelling it found zero outside the fields package's own tombstone tests.Reverse verification — direction predicted first, both legs from the committed state
Leg A (restore
'owner'to all three type faces). Predicted: Zod pin red on the refusal test; the two TS pins red at type-check, not vitest, and in the inverted shape — the expected error disappears rather than an illegal value being accepted. Observed exactly that:Leg B (drop the refusal branch, keep both deletions). Predicted: the retired field reaches a working person picker via delegation, so the tombstone assertions redden while the plain-text-input assertion stays green — the delegation road, not a text box. Observed:
Both legs restored byte-identically via
git checkout ddbf4d92e --followed by the edited paths (git diff HEADempty afterwards). No dist-ablation concern: these suites import from source, notdist/.Changeset
Real patch changeset on
@object-ui/types(published accept-set + type-surface shrink) and@object-ui/plugin-detail(behavior change). Noskip-changesetlabel — that label does not exist in this repo (#4912), and this PR publishes real changes anyway.Generated by Claude Code