Skip to content

docs(fields): tombstone MasterDetailField as a form-unreachable orphan - #5316

Merged
os-support-ai merged 1 commit into
mainfrom
claude/issue-4811-master-detail-field-tombstone
Aug 19, 2026
Merged

docs(fields): tombstone MasterDetailField as a form-unreachable orphan#5316
os-support-ai merged 1 commit into
mainfrom
claude/issue-4811-master-detail-field-tombstone

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Fixes#4811

What

Adds a tombstone header comment to packages/fields/src/widgets/MasterDetailField.tsx recording that the component:

  • is not in fieldWidgetMap (packages/fields/src/index.tsx);
  • is unreachable from any form path (registerField/registerAllFields only walk fieldWidgetMap's keys);
  • has its master_detail field-type key deliberately resolving to LookupField instead (the child-side FK must render as a single-value picker in create/edit forms, not this widget's one-to-many list model);
  • has its own test as its only in-repo consumer (complex-widgets.test.tsx, which renders it directly and proves nothing about form reachability).

Purpose: every future widgets/** sweep (the #3291 DOM-leak sweep / #3318 aria-invalid ledger / #4788 readonly-plumbing scan class — #4788 is where this orphan was originally found) stops paying the false-denominator tax of re-discovering and re-investigating an exported-but-unreachable component.

Scope is disposition 2 only, per the triage comment on #4811. Disposition 1 (deleting the widget and its public export) is explicitly out of scope — it is a breaking change to a published surface and is left to the maintainer, with the liveness evidence this tombstone documents. Nothing about the component's export, behavior, or registration changed.

Verification of the tombstone's claims (re-derived on current origin/main, not trusted from the issue body)

  1. Absent from fieldWidgetMap: grepped packages/fields/src/index.tsx — the master_detail entry (line ~2717) resolves to LookupField; no map entry anywhere resolves to MasterDetailField. Counter-probed the grep methodology against known-present keys ('text', 'number', 'select') to confirm the search itself works.
  2. Unreachable from any form path: registerField() looks up fieldWidgetLoaderByKey[fieldType] (backed by fieldWidgetMap) and warns+returns if absent; registerAllFields() only iterates Object.keys(fieldWidgetMap). Neither has a special case for MasterDetailField. A full-repo grep (all extensions, excluding node_modules/.git/dist) for MasterDetailField turns up only: the widget's own definition, its own test, the index.tsx barrel export + explanatory comment, an unrelated MasterDetailFieldMetadatatype in packages/types (a metadata shape, not a consumer of this component), and a changelog line in an old changeset.
  3. master_detailLookupField: confirmed at the map entry itself, with the adjacent comment explaining why (child-side FK needs a single-value lookup picker, not a one-to-many list).
  4. Only in-repo consumer is its own test: confirmed by the same full-repo grep — the sole render call is in complex-widgets.test.tsx.

All four claims hold; the tombstone was safe to write.

Verification

  • Reverse verification does not meaningfully apply to a comment-only change — no behavior exists to flip and re-flip. Skipped rather than staged as a ceremonial leg.
  • node scripts/check-changeset-presence.mjs → passes with the added empty-frontmatter changeset (.changeset/master-detail-field-tombstone-4811.md); node scripts/check-changeset-no-major.mjs → passes (no major declared).
  • Manual control-character scan (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]') on both changed files → clean (this repo has no scripts/check-nul-bytes.mjs, so the manual fallback was used).
  • Dependency closure build: pnpm --filter '@object-ui/fields^...' build → exit 0.
  • pnpm exec vitest run packages/fields/ --maxWorkers=2 (run from repo root, per the repo's own vitest-invocation guard) → 107 test files / 1802 tests passed.
  • pnpm --filter '@object-ui/fields' type-check → exit 0, no errors.
  • pnpm --filter '@object-ui/fields' lint → exit 0 (839 pre-existing warnings across the package, 0 errors; the one warning on the changed file, line 27's any, predates this change and sits outside the added comment block).

All commands run from the repository root; head commit 47c77c03b.


Generated by Claude Code

Re-derived on origin/main before writing this down: `fieldWidgetMap` has no
key resolving to MasterDetailField (the `master_detail` key deliberately
resolves to LookupField — the child-side FK must render as a single-value
picker, not the widget's one-to-many list model). `registerField`/
`registerAllFields` only walk `fieldWidgetMap`'s keys, so no form path
reaches it. Its only in-repo consumer is its own direct-render test
(complex-widgets.test.tsx).
Every future widgets/** audit (#3291 DOM leak sweep, #3318 aria-invalid
ledger, #4788 readonly-plumbing scan — the class this card was filed from)
re-discovers and re-investigates this component against its false
denominator. The header comment lets auditors skip the file on sight.
Disposition 1 (delete the widget + its public export) is a breaking change
to a published surface and is explicitly out of scope here — left to the
maintainer with the liveness evidence this tombstone documents.
Fixes#4811
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)25.3 KB350 KB
Entry fileindex-h-5c0RAO.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)9.83KB3.70KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)8.92KB3.41KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)29.33KB7.05KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.13KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.64KB2.21KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.79KB
auth (createAuthenticatedFetch.js)6.34KB2.43KB
auth (index.js)2.71KB1.22KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.02KB0.88KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)26.07KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.65KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)506.26KB113.31KB
core (index.js)4.11KB1.62KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)159.80KB44.34KB
fields (index.js)237.07KB59.46KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.42KB1.39KB
i18n (pickLocalized.js)3.69KB1.73KB
i18n (provider.js)23.13KB7.63KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)29.43KB7.15KB
i18n (useSafeTranslation.js)7.77KB3.13KB
layout (index.js)39.16KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.35KB3.31KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.42KB1.42KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.81KB0.83KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.62KB12.83KB
plugin-charts (index.js)64.75KB18.37KB
plugin-chatbot (index.js)181.21KB43.14KB
plugin-dashboard (index.js)127.92KB32.80KB
plugin-designer (index.js)212.39KB42.83KB
plugin-detail (index.js)241.46KB60.56KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)124.19KB30.20KB
plugin-gantt (index.js)164.10KB39.87KB
plugin-grid (index.js)198.22KB53.27KB
plugin-kanban (index.js)52.93KB14.60KB
plugin-list (index.js)111.66KB27.13KB
plugin-map (index.js)20.08KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.49KB11.93KB
plugin-timeline (index.js)26.68KB7.66KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)84.33KB20.61KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)36.10KB12.26KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.33KB0.69KB
react (schema-input.js)1.45KB0.83KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (index.js)4.77KB2.16KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)10.76KB3.17KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)6.92KB2.40KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)3.08KB1.53KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-support-ai
os-support-ai marked this pull request as ready for review August 19, 2026 13:22
@os-support-ai
os-support-ai added this pull request to the merge queueAug 19, 2026
Merged via the queue into main with commit 3f248bfAug 19, 2026
22 checks passed
@os-support-ai
os-support-ai deleted the claude/issue-4811-master-detail-field-tombstone branch August 19, 2026 13:22
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants

@os-support-ai@claude