Skip to content

fix(form): harden the spec↔runtime form-field chokepoint, derive SelectOption, complete FormFieldSchema (#3090 PR1) - #3091

Merged
os-zhuang merged 1 commit into
mainfrom
claude/form-field-chokepoint-pr1
Jul 31, 2026
Merged

fix(form): harden the spec↔runtime form-field chokepoint, derive SelectOption, complete FormFieldSchema (#3090 PR1)#3091
os-zhuang merged 1 commit into
mainfrom
claude/form-field-chokepoint-pr1

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#3090PR1(修缺陷,零破坏)。三块,每块都带 mutation-tested 闸门。

1. 枢纽补缺:normalizeSectionField 丢了四个 spec 17 键

最恶劣的一个:ADR-0089 把视图级可见性谓词的规范拼写定为 visibleWhen,而枢纽只 attach 弃用的 visibleOn —— 用规范拼写写的表单视图丢谓词,用弃用拼写反而能用。修法:视图级 visibleWhen 路由进视图层槽位(visibleOn),与对象级规则(fromObjectSchema 拷入的 visibleWhen 槽)AND 叠加而非互相覆盖(渲染器本来就分别评估两个槽位);两个拼写同时出现时规范拼写赢(saveMeta 按原样持久化,线上的元数据两种都有)。

连带堵住一个新暴露面:WizardForm 的最终提交重查只评估 visibleWhen 槽 —— 视图级谓词生效后,一个被视图隐藏的必填字段会在屏幕外卡死提交。重查现在与 form.tsx 一样折叠 visibleOn 槽(回归测试:谓词为假时提交放行)。

另三个键:dependsOn(级联声明,渲染器读顶层)、keyFielddisclosure(record/composite 控件配置,透传)。

闸门:sectionFields.spec-parity.test.ts 按 spec FormFieldSchema.in.shape 逐键行为断言(28 键各有一行,断言落点:helpTextdescriptionreadonlydisabled、双拼写→visibleOn 槽……),并双向比对键集 —— spec 加键红(unmapped)、退键红(stale)。已突变验证:删掉 keyField 映射行,对应行即红。

2. SelectOptionSchema 改为 spec 派生(台账 124 → 122)

旧手抄件静默剥掉 color(@object-ui/fields 实际渲染 badge/dot 颜色)、default、以及逐选项 visibleWhen 门控 —— 一个带权限门控的选项过了 objectui validate,门却被扒了。现在 spec 键按引用流入,两个钉住的有意分歧(value 放宽到 string|number|boolean;visibleWhen 保持 #2212 wire 契约,不用 spec 的 envelope 规范化管道,输出形状不变)+ 两个 UI 扩展(disabled/icon)。TS SelectOptioncolor/default

台账:SelectOption/SelectOptionSchema 从 DEBT 移入 ALLOW(带理由与守卫指针)。parity 测试四向守卫:spec 加键、退键、认领扩展名、自行放宽 value,任一发生即红。

3. FormFieldSchema(runtime 词汇)补齐自家契约

CLI 唯一强制执行的表单字段契约此前只覆盖 TS 接口 13 个声明键、且要求接口里可选的 type 必填:渲染器接受的元数据过不了校验,而 visibleWhen: 42 这种错值被 strip 静默吞掉。补齐 widget/dependsOn/hidden/readonly/visibleOn/visibleWhen/readonlyWhen/requiredWhen/span,type 改可选。

边界钉死:{ field: 'email' }(spec 词汇)仍然拒绝 —— 双层边界属于枢纽,不属于双键读(PR2 只改错误文案,不改判定)。set-coverage 断言钉住键集(TS 接口带 index signature,两个方向的可赋值性探针都会说谎 —— 见 checker 头注 lie #3 —— 故按 #3017 模板用 pinned list)。

验证

  • 592 tests 过(types + plugin-form 全量),新增 43 个断言
  • types/plugin-form/components/cli 四包 tsc 干净;改动文件 eslint 0 errors
  • 全部治理脚本绿:spec-symbols(122)、type-check-coverage、lint-coverage、changeset-fixed

Closes 部分 #3090(PR1 复选框)。关联 objectstack#4115。

🤖 Generated with Claude Code

…ctOption, complete FormFieldSchema (#3090)

normalizeSectionField dropped four spec-17 FormField keys — worst of all
the ADR-0089 canonical `visibleWhen` spelling (the deprecated `visibleOn`
worked): route it into the view-level slot so it ANDs with the
object-level rule, carry `dependsOn`/`keyField`/`disclosure`, and fold
the same slot into WizardForm's final-submit gate so a view-hidden
required field can no longer block submission from off-screen. A
behavioral parity gate now walks the spec FormFieldSchema key set both
ways (added key = unmapped failure, retired key = stale failure),
mutation-tested.

SelectOptionSchema derives from @objectstack/spec/data by reference —
it used to strip `color` (which @object-ui/fields renders), `default`,
and the per-option `visibleWhen` gate — with pinned divergences (value
widened for UI forms, visibleWhen on the #2212 wire contract) and
documented UI-only extensions; ledger 124 → 122 (SelectOption,
SelectOptionSchema → ALLOW with drift guards).

FormFieldSchema now covers every key the FormField interface declares
and makes `type` optional to match it, so `objectui validate` stops
rejecting metadata the renderer accepts and stops stripping typo'd
predicates; `{ field: … }` (spec vocabulary) stays rejected, pinning
the two-layer boundary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 31, 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 Jul 31, 2026 4:15am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 27.9 KB 350 KB
Entry file index-1dzhbejX.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.26KB 2.99KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
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.12KB 3.41KB
auth (LoginForm.js) 17.86KB 5.29KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.43KB 2.09KB
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) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.25KB 0.53KB
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) 472.44KB 103.27KB
core (index.js) 2.16KB 0.78KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 136.34KB 34.64KB
fields (index.js) 222.07KB 54.35KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.46KB 0.96KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 5.37KB 1.72KB
i18n (useObjectLabel.js) 25.17KB 5.80KB
i18n (useSafeTranslation.js) 3.26KB 1.44KB
layout (index.js) 38.44KB 10.66KB
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.05KB 1.53KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.76KB 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 (retry.js) 3.48KB 1.61KB
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) 44.90KB 12.35KB
plugin-charts (index.js) 60.52KB 17.11KB
plugin-chatbot (index.js) 180.09KB 42.72KB
plugin-dashboard (index.js) 111.59KB 28.74KB
plugin-designer (index.js) 210.51KB 42.50KB
plugin-detail (index.js) 221.81KB 54.28KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 110.95KB 26.75KB
plugin-gantt (index.js) 162.26KB 39.53KB
plugin-grid (index.js) 182.21KB 48.24KB
plugin-kanban (index.js) 47.82KB 13.18KB
plugin-list (index.js) 103.85KB 24.80KB
plugin-map (index.js) 16.80KB 5.24KB
plugin-markdown (index.js) 13.65KB 4.67KB
plugin-report (index.js) 40.32KB 10.53KB
plugin-timeline (index.js) 25.75KB 7.32KB
plugin-tree (index.js) 8.36KB 2.81KB
plugin-view (index.js) 83.54KB 20.39KB
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) 19.28KB 6.38KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 3.47KB 1.54KB
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 (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 (index.js) 2.07KB 0.99KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 0.20KB 0.18KB
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) 1.08KB 0.64KB
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

@os-zhuang
os-zhuang merged commit 7639a61 into main Jul 31, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/form-field-chokepoint-pr1 branch July 31, 2026 04:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant