Skip to content

fix(app-shell): expand the nested config.columns union into per-column diagnostics - #3677

Merged
yinlianghui merged 2 commits into
mainfrom
claude/issue-3626-nested-union-diag
Aug 7, 2026
Merged

fix(app-shell): expand the nested config.columns union into per-column diagnostics#3677
yinlianghui merged 2 commits into
mainfrom
claude/issue-3626-nested-union-diag

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#3626

问题

config.columnsstring[] | ColumnDef[] —— 一个没有判别式的 union。Zod 对 union 失败只产出一条 issue(Invalid input),成员的真实诊断埋在 issue.errors 里,于是任何 columns 错误都塌成 config.columns / Invalid input,创建路和编辑路都是

PR #3624 修的是根级 union,并把这条嵌套残留写进了「已知残留」。它比 #3606 轻:path 是真的,SchemaForm 的内联高亮和 Monaco 定位都还能工作 —— 用户被带到出错的字段,但不知道哪一列、哪个键、期望什么

测量(先测量,后设计)

把整个 view 家族的 Zod 树走了一遍(@objectstack/spec 17.0.0-rc.5):16 个无判别式的嵌套 union,其中 columns唯一一个两个成员都是数组的。这决定了规则能安全地写成什么样。

判别式取值自身的首元素:一份 columns 要么是字段名列表、要么是列对象列表,首元素说了算。这与根级的 viewKind 是同一类规则(关于作者写了什么的事实),不是#3606 实测否决的「按 issue 数 / 深度」排序启发式 —— 后者是把各成员的错误组互相比较。

边界,全部实测而非假设

边界实测结果处置
columns: []两个成员都通过,union 成功空数组的判别问题结构上不存在 —— 根本走不到失败路径
columns: [42] / [null]首元素既非 string 也非 object不判别,保留原消息(两成员本就给出对等的拒绝,选一个等于凭空发明偏好)
columns: 'nope'不是数组不判别(两成员确实一致,但「全体一致就提升」是 issue 的方向 1,另一套机制,本 PR 不做)
混合数组首元素定变体['name', {field: 1}]config.columns.1 —— 作者正在写的那张列表里真正坏掉的那个元素

收窄:必须不能替 config.sort 说话

config.sortstring | ColumnSort[],同样两个成员、同样可以是数组值。对 sort: ['name'],只看首元素会选中纯 string 成员并报「expected string, received array」—— 字面为真,却是对一个正确写了数组的人说的最没用的话。

所以加了一条逐组独立的范畴判定:在 union 节点上直接拒绝了值的类型(自身相对根 path 为空的 invalid_type)的成员,压根没看过内容,内容就不能作为它的证据,因此不是候选。这条把 sortfilter[].valuegantt.tooltipFields[] 等全部挡在外面。它是筛选适用范围,不是挑选赢家 —— 赢家只由内容决定。

修前 / 修后(两条路逐一实测)

body修前 path + message(两路相同)修后 path + message(两路相同)
columns: [{ field: 123 }]config.columns / Invalid inputconfig.columns.0.field / expected string, received number
columns: [{}]config.columns / Invalid inputconfig.columns.0.field / expected string, received undefined
columns: ['a','b',42]config.columns / Invalid inputconfig.columns.2 / expected string, received number
columns: [{field:'a'},'b']config.columns / Invalid inputconfig.columns.1 / expected object, received string
columns: [{field:'a',summary:{type:'bogus'}}]config.columns / Invalid inputconfig.columns.0.summary / Invalid input
容器 list.columns: [{field:123}]list.columns / Invalid inputlist.columns.0.field / expected string, received number
columns: [42]config.columns / Invalid input不变(判别不出,见上表)
sort: ['name']config.sort / Invalid input不变(收窄条款挡住)

创建路 / 编辑路收敛一致:修前两路都塌在 config.columns,修后两路逐字节相同 —— 这是测试里的 bothGates 断言(expect(edited.issues).toEqual(created.issues)),不是论证。两路走的是不同路线:创建路上这个 union 是顶层 issue,编辑路上它在根成员选中之后再下一层;path 前缀拼接正是被测的东西。

倒数第二行值得单说:columns[0].summaryenum | {type, field},值是对象,数组规则对它无话可说,于是保留 Zod 自己的消息 —— 但已经被正确定位到 config.columns.0.summary,而不再是 config.columns。这也是下降的真实边界:不是深度计数器,而是「规则在这里没有话说」。

判定奇偶:只有呈现变了

展开逻辑仍在 issue → SchemaFormIssue映射内部,位置在 ok 判定之后、所有既有过滤器之后 —— 驱动 ok 的那组 raw issues 一个都没动。判定不是「测出来没变」,而是结构上不可能变。仍按 #3624 的手法钉成 pin(8 个 columns 形状 × ok),它修前修后都绿,这正是它的意义。

逆向验证(先预测,后运行)

预测写在跑之前,两个方向都命中:

扰动 A —— 还原实现到 origin/main:预测 7 红(5 CANARY + 1 噪音控制 + 1 summary 深度锚点),其余绿。实际:恰好这 7 条红,一条不多一条不少。

扰动 B —— 只删掉收窄条款那一行(if (groups.some(memberRejectedNodeType)) return null;):config.sort 那条钉子在扰动 A 下两个方向都是绿的(它钉的是「不变」),所以 A 证明不了它有效 —— 需要专门的扰动。预测:只有它一条红。实际:

Tests 1 failed | 37 passed (38)
× does NOT answer for `config.sort` — one member never accepted the array
- "message": "Invalid input",
+ "message": "Invalid input: expected string, received array",
"path": "config.sort",

即被预测的那句误选原样出现。如实注明:边界类与收窄类钉子([42]'nope'、空数组、filter value、判定奇偶)在扰动 A 下修前修后都绿 —— 它们是防回归的那一半,不是「修前红修后绿」的那一半。

测试

clientValidation.viewDiagnostics.test.ts只加不改(#3624 的 18 条断言一行未动),新增 20 条:5 CANARY + 1 噪音控制 + 2 收窄 + 4 边界 + 8 判定奇偶。

vitest run packages/app-shell/src/views/metadata-admin/ → 128 passed, 1207 passed | 1 skipped
pnpm --filter @object-ui/app-shell type-check → clean
eslint(两个改动文件) → clean
node scripts/check-control-bytes.mjs → OK (3677 files)
node scripts/check-changeset-no-major.mjs / -fixed.mjs → OK

clientValidation.viewShapes.test.tsclientValidation.viewDiagnostics.test.ts 的现有断言原样绿。

已知残留(未修,不在本单范围)

  • sort: [{field:'n', order:'bogus'}] 这类:只剩一个成员接受了值的类型,理论上可以无歧义地选中它,但那是「唯一候选」规则,不是本单授权的内容判别。仍保留 config.sort / Invalid input,已钉住。
  • 全体一致提升(issue 的方向 1)与 spec 侧提供判别入口(方向 2 / objectstack#6391)都未做 —— 后者是契约优先的正解,跨仓。

Generated by Claude Code

…umn diagnostics
`config.columns` is `string[] | ColumnDef[]` with no discriminant, so Zod
collapsed every rejection into one issue on the field itself — `config.columns`
/ `Invalid input` — on both the create and the edit gate. #3606 expanded the
ROOT union only and recorded this as a known residual.
Select the member the value's own first element elects (field names vs column
objects) and report that member's issues at their draft-absolute path, composing
the union node's path as the prefix. Narrowed to unions that really are "array
of A or array of B": a member that rejected the node's TYPE outright never
looked at the contents, so it is not a candidate — which keeps `config.sort`
(`string | ColumnSort[]`) and the other neighbours untouched.
Presentation only; the verdict is still the one gate's.
Fixes#3626
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
@vercel

vercelBot commented Aug 7, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 7, 2026 8:11pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)28.1 KB350 KB
Entry fileindex-Dz25vJb1.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)8.66KB3.13KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)7.57KB2.97KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)22.10KB4.37KB
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)35.76KB9.11KB
auth (createAuthenticatedFetch.js)4.37KB1.69KB
auth (index.js)2.35KB1.07KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)4.91KB0.87KB
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)480.72KB105.64KB
core (index.js)2.96KB1.13KB
create-plugin (index.js)9.28KB2.98KB
data-objectstack (index.js)137.51KB35.11KB
fields (index.js)230.87KB56.83KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)2.65KB1.06KB
i18n (pickLocalized.js)1.70KB0.83KB
i18n (provider.js)9.48KB3.27KB
i18n (useObjectLabel.js)26.14KB6.07KB
i18n (useSafeTranslation.js)4.52KB1.96KB
layout (index.js)38.53KB10.71KB
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)8.75KB3.06KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.41KB1.44KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.71KB3.79KB
plugin-calendar (index.js)44.98KB12.37KB
plugin-charts (index.js)61.04KB17.31KB
plugin-chatbot (index.js)180.09KB42.72KB
plugin-dashboard (index.js)115.50KB29.96KB
plugin-designer (index.js)210.51KB42.51KB
plugin-detail (index.js)232.79KB57.42KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)112.10KB27.10KB
plugin-gantt (index.js)162.55KB39.57KB
plugin-grid (index.js)186.61KB49.34KB
plugin-kanban (index.js)48.30KB13.28KB
plugin-list (index.js)105.12KB25.48KB
plugin-map (index.js)16.81KB5.24KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)40.58KB10.58KB
plugin-timeline (index.js)25.76KB7.33KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)84.03KB20.55KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.67KB2.37KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)19.28KB6.38KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.02KB0.55KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)4.47KB2.03KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
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 (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)2.71KB1.34KB
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

…-union comment
The comment named `ColumnsSchema` as the thing a spec reorder would touch; no
such export exists. Refer to the union's members instead of inventing a name a
future reader would grep for and not find.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)28.1 KB350 KB
Entry fileindex-Dz25vJb1.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)8.66KB3.13KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)7.57KB2.97KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)22.10KB4.37KB
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)35.76KB9.11KB
auth (createAuthenticatedFetch.js)4.37KB1.69KB
auth (index.js)2.35KB1.07KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)4.91KB0.87KB
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)480.72KB105.64KB
core (index.js)2.96KB1.13KB
create-plugin (index.js)9.28KB2.98KB
data-objectstack (index.js)137.51KB35.11KB
fields (index.js)230.87KB56.83KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)2.65KB1.06KB
i18n (pickLocalized.js)1.70KB0.83KB
i18n (provider.js)9.48KB3.27KB
i18n (useObjectLabel.js)26.14KB6.07KB
i18n (useSafeTranslation.js)4.52KB1.96KB
layout (index.js)38.53KB10.71KB
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)8.75KB3.06KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.41KB1.44KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.71KB3.79KB
plugin-calendar (index.js)44.98KB12.37KB
plugin-charts (index.js)61.04KB17.31KB
plugin-chatbot (index.js)180.09KB42.72KB
plugin-dashboard (index.js)115.50KB29.96KB
plugin-designer (index.js)210.51KB42.51KB
plugin-detail (index.js)232.79KB57.42KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)112.10KB27.10KB
plugin-gantt (index.js)162.55KB39.57KB
plugin-grid (index.js)186.61KB49.34KB
plugin-kanban (index.js)48.30KB13.28KB
plugin-list (index.js)105.12KB25.48KB
plugin-map (index.js)16.81KB5.24KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)40.58KB10.58KB
plugin-timeline (index.js)25.76KB7.33KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)84.03KB20.55KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.67KB2.37KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)19.28KB6.38KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.02KB0.55KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)4.47KB2.03KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
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 (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)2.71KB1.34KB
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

@yinlianghui
yinlianghui marked this pull request as ready for review August 7, 2026 20:21
@yinlianghui
yinlianghui added this pull request to the merge queueAug 7, 2026
Merged via the queue into main with commit 949b2f1Aug 7, 2026
19 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-3626-nested-union-diag branch August 7, 2026 20:22
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Aug 10, 2026
… the value (objectstack-ai#3693)
PR objectstack-ai#3677 narrowed its content rule so it could not speak for `config.sort`,
which left every union where some member rejected the value's type outright
collapsed to `Invalid input`. When exactly one member accepted the type,
naming it is a fact rather than a preference: it is the only member that read
the value, so it is the only one whose complaint can be about what the author
wrote.
`sort: [{field: 'n', order: 'bogus'}]` now reports `config.sort.0.order` with
the spec's own `expected one of "asc"|"desc"` instead of `config.sort` /
`Invalid input`. The same holds for a `columns[].summary` written as a bad
enum string, a form `sections[].fields[]` entry missing its `field`, and an
array `filter[].value` whose offending element is now addressed directly.
The two nested rules are disjoint rather than layered: censusing the members
by objectstack-ai#3677's categorical test, k=1 is this rule, k=every-member is the content
rule, and everything else is nobody's. Order is unobservable while both guards
hold, and is written content-first so objectstack-ai#3677's narrowing line stays the thing
that fails when removed.
Verdicts are untouched — this runs inside the issue mapping, downstream of
`ok`. Two of objectstack-ai#3677's narrowing pins are superseded and re-pinned: they were
asserting that no rule spoke there, which is exactly what this changes.
Fixesobjectstack-ai#3678
Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

metadata-admin:嵌套 union(config.columns)的诊断仍塌成「Invalid input」——创建路/编辑路都中

2 participants

@yinlianghui@claude