Skip to content

chore(sweep): retire four zero-consumer declared surfaces — dead-surface deletions batch 3 (#4328) - #4366

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-4328-dead-surface-batch3
Aug 11, 2026
Merged

chore(sweep): retire four zero-consumer declared surfaces — dead-surface deletions batch 3 (#4328)#4366
yinlianghui merged 1 commit into
mainfrom
claude/issue-4328-dead-surface-batch3

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #3775
Fixes #3778
Fixes #4288
Fixes #3773
Fixes #4328

Dead-surface deletions batch 3. One claim, one PR, one Fixes line per member. All four members ship: every premise was re-measured at this branch point (7e4f0e530, not the triage refs — main moved ~150 PRs on 2026-08-11), and none had drifted.

# Member Resolution Premise at 7e4f0e530
1 #3775 deleted zero consumers — only the file itself and its export * line
2 #3778 deleted (5 sites) no read path populates views:{object}; listViews is uncached
3 #4288 removed (not demoted) zero production reads; the sole non-test hit is a test comment
4 #3773 deleted zero producers, zero consumers; only the memo dep list named them

git diff --stat is 13 files: 4 member rows + 2 pins + 1 changeset. Nothing else.


Member 1 — #3775: the second mergeViewsIntoObjects in @object-ui/core

Evidence (git grep -n "mergeViewsIntoObjects" -- packages apps examples, at 7e4f0e530, excluding dist/node_modules): every hit outside the deleted file is app-shell's own live copy, its tests, or prose.

packages/app-shell/src/providers/MetadataProvider.tsx:222:export function mergeViewsIntoObjects(...)
packages/app-shell/src/providers/MetadataProvider.tsx:697:  const merged = views.length ? mergeViewsIntoObjects(objs, views) : objs;
packages/app-shell/src/providers/MetadataProvider.merge.test.ts        (9 hits, imports './MetadataProvider')
packages/app-shell/src/views/ObjectView.defaultViewIdentity.test.tsx   (imports '../providers/MetadataProvider')
packages/app-shell/src/views/ObjectView.setDefaultViewIdentity.test.tsx(imports '../providers/MetadataProvider')
packages/data-objectstack/src/index.ts:3089:  // ... mirroring MetadataProvider.mergeViewsIntoObjects   ← a comment
packages/core/src/utils/merge-views-into-objects.ts:20:export function mergeViewsIntoObjects(...)  ← the deleted copy

git grep -n "merge-views-into-objects" returns exactly one line: the barrel re-export at packages/core/src/index.ts:34. Both are gone; app-shell's copy — the one at MetadataProvider.tsx:697 — is untouched.

Member 2 — #3778: five invalidations of a key nothing populates

The triage anchors still held line-for-line at this branch point (:3022, :3183, :3243, :3274, :3291), so no re-anchoring was needed.

Evidence. git grep -n 'views:'overpackages apps examplesreturns those fiveinvalidate` lines and nothing else — no producer anywhere in the repo. Every cache key the adapter actually reads back:

line cacheKey reader
2931 view-overrides:{objectName} listViewOverrides
2968 view:{objectName}:{viewId} getView
3018 view:{objectName}:{viewId} updateViewConfig (invalidation)
3310 app:{appId} getApp
3331 page:{pageId} getPage
3739 {category}:{name} generic metadata read

No views: prefix among them, and the file has no metadataCache.set/prime at all. listViews fetches client.meta.getItems('view') directly with no metadataCache.get wrapper — genuinely uncached, and left that way. Whether it should be cached is a separate card, not this sweep.

Pin (packages/data-objectstack/src/viewCacheInvalidation.pin.test.ts, 6 cases): listViews still hits the transport on every call and consults no cache key (behaviour unchanged — this deletion changes no request count); the surviving invalidations still name exactly the keys with readers (updateViewConfig → both, updateView draft + published → the getView key, deleteView → the getView key); and no write path names a views: key. createView is asserted as "no views: key" rather than "no invalidation at all", deliberately — see the finding below.

Member 3 — #4288: RoleDefinition.permissions removed, not demoted

Evidence. git grep -n "\.permissions" over packages apps examples at 7e4f0e530: the only hits on a RoleDefinition are three test comments documenting the dormancy. Every other hit is a different type (d.permissions in AgentPreview, contract.permissions, version.permissions, counts.permissions). The four consumers of RoleDefinition[]evaluator.ts:18, resolveRoles at :92, store.ts:34, PermissionProvider.tsx:22 — read name and inherits only.

The member card offered removal or demotion to optional. Removal, on all three axes:

  • Real business need — measured, none. Zero readers; every grant the evaluator applies comes from ObjectPermissionConfig.roles, keyed by object. There is no producer either: five fixtures across three packages independently omitted the field until PR fix(permissions): type-check its tests (#4040 tranche 1) #4287 gave them permissions: [], and every case still asserted what its author meant, because no code path was ever going to look.
  • Long-term soundness — the repo had two declared homes for "what a role may do" and one wired one. Removal collapses that to one contract. Demotion keeps the second home and merely stops the compiler asking about it, which is the same declared-not-enforced state with the evidence removed.
  • Hard to get wrong — this is what decides it. With the field gone, writing role-attached grants is a compile error; with it optional, it is accepted and silently ignored. Measured, not asserted: restoring permissions: [] to one fixture turns pnpm --filter @object-ui/permissions type-check red with error TS2353: Object literal may only specify known properties, and 'permissions' does not exist in type 'RoleDefinition'. A lenient optional key is exactly where AI-authored metadata errors hide.

RoleDefinition's doc comment now records where grants live and that the second home was retired, so the next author does not re-declare it.

Blast radius (the PM rider — this row's, not scope creep): the permissions: [] fixture convention from #4287, followed by #4326/#4338, becomes an excess-property error. Seven fixture lines in five files are cleaned, and the three comments that explained the empty array are rewritten to say where grants actually come from.

Changeset shape. No major conflict arises: AGENTS.md §版本号策略 rules that objectui's own breaking changes are marked minor with the breaking semantics spelled out in the body, the major being reserved for tracking @objectstack. That is what this changeset does, so there was nothing to escalate.

Member 4 — #3773: RecordContextValue.loading / error

Evidence. Zero producers: the two mount points pass neither — RecordDetailView.tsx:2054 passes objectName / recordId / data / objectSchema / dataSource / embedded / refresh / headerSystemActions / isFavorite / onToggleFavorite, and PagePreview.tsx:192 a subset. git grep -n -A 12 "RecordContextProvider" filtered to loading|error across packages apps returns no prop on any provider, in source or tests. Zero consumers: git grep -n "ctx\.loading\|ctx\.error\|recordCtx\.loading\|recordCtx\.error" returns one hit, MetadataProvider.tsx:730 — the Metadata context, a different context object, exactly as the triage comment said. Only the memo dep list named the two keys.

Pin (packages/react/src/context/__tests__/RecordContext.valueShape.pin.test.tsx). The provider spreads its props and hands consumers a memoized object, which makes the dep list a hand-maintained mirror of the interface — the drift that let these two keys sit in it for months. The pin joins both halves: a compile-time assertion that its key list is exactly keyof RecordContextValue (checked by tsconfig.test.json, which this package's type-check chains), and a runtime sweep proving each listed key really re-memoizes. A new key cannot land half-wired, and a retired one cannot come back silently.


Reverse verification

Deletions are proved by the zero-consumer greps above plus a green ladder; where a deletion could silently change behaviour, the direction was predicted before running it and each came back red as predicted (fix taken out with git checkout / edit-then-restore, never git stash):

what was put back expected observed
permissions: [] on one role fixture tsc red store.test.ts(17,36): error TS2353: … 'permissions' does not exist in type 'RoleDefinition'
loading?: boolean re-added to RecordContextValue tsc red on the key-list assert RecordContext.valueShape.pin.test.tsx(65,3): error TS2344: Type 'false' does not satisfy the constraint 'true'
value.isFavorite dropped from the memo dep list one pin case red × re-memoizes when isFavorite changesexpected {…} not to be {…}
one views:{object} invalidation restored in deleteView two pin cases red expected [ 'views:account', 'view:account:v1' ] to deeply equal [ 'view:account:v1' ] and expected [ 'views:account' ] to deeply equal []

Verification

Downstream sweeps run honestly — these deletions sit upstream of most of the repo, so the type gate was run repo-wide rather than scoped.

turbo run build   --filter=!@object-ui/site --concurrency=2   →  43 successful, 43 total
turbo run type-check --concurrency=2                          →  78 successful, 78 total
turbo run lint (7 touched packages)                           →  7 successful, 0 errors

vitest run packages/react/ packages/data-objectstack/ packages/permissions/ \
           packages/types/ packages/core/ packages/plugin-list/ packages/plugin-detail/
  →  Test Files 290 passed (290) | Tests 4335 passed (4335)

consumers of the changed surfaces (app-shell MetadataProvider + ObjectView view-identity +
RecordDetailView + PagePreview, components page-header*, plugin-grid predicate parity,
layout, console record-block reach)
  →  Test Files  33 passed (33) | Tests 372 passed (372)

node scripts/check-changeset-presence.mjs   ✅ 12 source files of 7 released packages, 1 changeset
node scripts/check-changeset-fixed.mjs      ✅
node scripts/check-changeset-no-major.mjs   ✅
node scripts/check-control-bytes.mjs        ✅ 4080 tracked text files

The repo-root vitest form is the one AGENTS.md §测试纪律 mandates; heavy phases ran under the shared verification lock.

Out of scope, filed as their own cards


Generated by Claude Code

Dead-surface deletions batch 3. Every member was re-measured at this branch
point (7e4f0e5) and each is removed rather than left as an authoring surface
whose values nothing reads.

- core: delete the second `mergeViewsIntoObjects` and its `export *` (#3775).
  Zero in-repo consumers; the live copy is MetadataProvider's, untouched.
- data-objectstack: delete the five `views:{object}` invalidations (#3778).
  No read path populates that key — `listViews` is uncached — so all five were
  permanent no-ops. Pinned alongside the two keys that do have readers.
- types: remove `RoleDefinition.permissions` (#4288). Grants live in
  `ObjectPermissionConfig.roles`; the field was required and read by nothing,
  so five fixtures across three packages declared an empty array for it.
- react: remove `RecordContextValue.loading` / `error` and their memo dep-list
  wiring (#3773). Zero producers, zero consumers. The new pin ties the dep list
  to `keyof RecordContextValue` at compile time and at runtime, so neither a
  half-wired new key nor a resurrected dead one can land silently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 11, 2026 10:05pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 29.7 KB 350 KB
Entry file index-B3R4xE91.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 489.12KB 108.41KB
core (index.js) 2.99KB 1.14KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 149.75KB 39.78KB
fields (index.js) 228.37KB 56.62KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.33KB 1.20KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 4.52KB 1.96KB
layout (index.js) 38.98KB 10.85KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 45.23KB 12.45KB
plugin-charts (index.js) 62.18KB 17.67KB
plugin-chatbot (index.js) 180.33KB 42.79KB
plugin-dashboard (index.js) 121.60KB 31.63KB
plugin-designer (index.js) 210.91KB 42.67KB
plugin-detail (index.js) 238.95KB 59.76KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 114.58KB 27.68KB
plugin-gantt (index.js) 164.14KB 39.98KB
plugin-grid (index.js) 188.00KB 49.94KB
plugin-kanban (index.js) 48.60KB 13.41KB
plugin-list (index.js) 110.10KB 26.74KB
plugin-map (index.js) 18.05KB 5.80KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.60KB 10.58KB
plugin-timeline (index.js) 26.21KB 7.52KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.03KB 20.55KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 23.71KB 7.96KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.23KB 0.66KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 3.05KB 1.52KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@yinlianghui
yinlianghui marked this pull request as ready for review August 11, 2026 22:18
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 11, 2026
Merged via the queue into main with commit d9d3463 Aug 11, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-4328-dead-surface-batch3 branch August 11, 2026 22:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment