fix(fields): retire the owner field-type alias with a loud tombstone (#4814) - #4915
Merged
Conversation
#4814) `owner` was a synonym for `user` with zero behavioral delta (both resolved to the same UserField widget) and is not a member of `@objectstack/spec`'s closed FieldType, so no object schema could declare it. Three code faces had already drifted apart on the word. Deleting the alias alone would have been absorbed by two silent tails (`mapFieldTypeToFormType`'s `|| 'field:text'` and `resolveFormWidgetType`'s `: 'text'`), each handing back a working text input with no gate turning red. So the retired spelling now resolves to a registered tombstone widget that renders a visible refusal and logs the migration once per spelling. Migration: `{ type: 'user', name: 'owner' }`. Also drops the zero-consumer `grid` claim from the `dataSource` TSDoc. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DbRmJD3iPhXjKr6vhd4Qkv
Surfaced by paramValueShape's own drift guard ("no stale contract entries
pointing at removed widget types") once `owner` left FORM_FIELD_TYPES. A value
shape declared for a type no form can render is a contract for a param nobody
can author.
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
|
This was referenced Aug 17, 2026
#4814) CI Test shard 4/4 caught this: the snapshot is derived from ALL_TYPES (FORM_FIELD_TYPES union the spec FieldType enum), and `owner` left that universe entirely once the retirement removed it from FORM_FIELD_TYPES — it was never a member of the spec enum, which is this card's own premise. So it is not re-bucketed, it is simply gone; the retirement stays pinned in one place, `fields/src/__tests__/owner-retired.test.tsx`. Also refreshes the universe note, which still named `owner` as a form-only key the spec enum misses. Not touched: `INLINE_ROUTED_FIELD_TYPES` still lists 'owner'. The snapshot does not derive from it, so removing it here would be forcing an unrelated source; tracked in #4914. 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 05:37
This was referenced Aug 17, 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 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.
Fixes #4814
Retires the
ownerfield-type alias per the 2026-08-17 maintainer ruling A′ (comment 5310924344), with a loud tombstone rather than a silent deletion.Why a tombstone, and not just three deletions
ownerwas a synonym foruserwith zero behavioral delta — both resolved to the sameUserFieldwidget — and it is not a member of@objectstack/spec's closed 48-memberFieldType, so no object schema could declare it. It was reachable only through hand-written SDUI.Deleting the alias alone would have been absorbed by two silent tails:
mapFieldTypeToFormType's|| 'field:text'andresolveFormWidgetType's: 'text'. Each hands back a working plain text input. Anyone who had writtentype: 'owner'— including an AI author copying it out of a doc — would have shipped a text box believing they shipped a person picker.So the retired spelling now resolves to a registered tombstone widget that renders a visible refusal naming the migration, and logs the same prescription to the console once per spelling. Both authored spellings converge on it, because
form.tsx'srenderFieldComponentperforms the sameComponentRegistry.get('field:owner')lookup for awidget: 'field:owner'hint and for a hand-writtentype: 'owner'.This is also the designed answer to the surface that could not be measured: the
cloudrepo was never scanned (the measuring session had no credentials). Any consumer living there now fails loudly and nameably instead of degrading in silence.Migration:
{ type: 'user', name: 'owner' }— the field NAME carries the ownership meaning, the type carries the widget.UserFieldandUserCellRendererare untouched; only the synonym is gone.Reverse verification — direction predicted first
Predicted: ablating the tombstone (i.e. the naive "just delete the alias" execution) turns the new pin red on the loudness assertions specifically, while the pre-existing gates stay green.
Observed exactly that. With the retired-type branch and the tombstone registration removed:
The second file there is
field-type-coverage.test.ts, which stayed green under the ablation — reproducing the fork report's central claim that no existing gate catches a silent retirement. That asymmetry is the whole argument for the tombstone. Ablation restored byte-identically from the commit (git checkout branch -- paths, emptygit diff HEAD).Catalog disposition (the ruling left this to the implementer)
examples/schema-catalog/src/schemas/fields-user/record-owner-read-only.jsonmigrates totype: 'user'rather than being deleted with theuser.mdxsection.Reason: measured against
single-user-selection, the example's unique contribution isreadonly: true, not theownerspelling — nothing else infields-userdemonstrates a read-only person field. Deleting it to dedupe would have removed real coverage while the actual duplicate (a second spelling of one type) is what this card is removing. Theuser.mdxsection is retitled "Read-Only Record Owner" and now states plainly that a record-owner field is auserfield whose name carries the meaning. The hosted suite stays green.Scope note — a measured gap in the ruled enumeration
Re-sweeping on this branch found the ruled "three code consumers" enumeration is materially incomplete: roughly 13 further faces treat
owneras a live field type, including a published runtime Zod enum (@object-ui/typesreports.zod.ts) that still accepts it, and two published TS unions that are the code twins of the very doc unions this PR shrinks. All were present at the fork report's own base commit1ef236e18— pre-existing faces the earlier sweep did not reach, not new consumers.They are not fixed here (8 packages, well outside this card's claimed file surface, with collision risk that was never checked for those files). Filed as #4914 with the full list and line numbers. This PR is coherent and strictly-better standalone: the retirement fails loudly, so the residual faces are surfaced by the tombstone rather than hidden by it.
One face beyond the ruled list is included, because it turned red here and its own drift guard prescribed the fix:
app-shell/src/utils/paramValueShape.tsdeclared a value shape forowner, and the guard "no stale contract entries pointing at removed widget types" failed the momentownerleftFORM_FIELD_TYPES.Also in this batch
The
gridzero-consumer claim inpackages/fields/src/widgets/types.tsdataSourceTSDoc is corrected, per the standing half of the prior ruling. Independently re-verified:GridField.tsxcontains zerodataSourceoccurrences, and no data-source table ever held the key.Verification
Union run at
59b30a100(the final commit):Dependency closures were built before judging (
--filter '@object-ui/plugin-grid^...' --filter '@object-ui/app-shell^...' build) — the first typecheck pass failed only on missingdist/*.d.tsand went green on rebuilt closures. Heavy runs held/tmp/os-heavy-verify.lock.Patch changeset included:
@object-ui/fields,@object-ui/plugin-grid,@object-ui/app-shellall change published behavior.Generated by Claude Code