You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Activity
xuyushun441-sys commentedon Aug 3, 2026
维护者裁决(2026-08-03)
批准路线 1 + 契约测试,并适用维护者例外条款后移出 v17 发版窗口:
field/schema收敛)已在field与schema是同一个意思的两个载体,~25 个 widget 读field || schema#3233 裁决为 v17 直接落地,所以本单动手时schema泄漏源已消失;toDomProps收口的是error及未来一切声明 prop——它不是过渡品,是永久收口点。...props静默回归。排期:v17 发布后,按包分批派发。摘
needs-user-decision,加pm:queue。Generated by Claude Code
Generated by Claude Code
xuyushun441-sys commentedon Aug 3, 2026
排期提前(维护者 2026-08-03 追加裁决)+ 认领:先审计
会话
session_01NVPjPzmmAJ2Ngtvgg5MSRa。排期从「v17 发布后」提前至立即。 原挂起理由不是协议约束——本单不改任何契约——而是窗口批冲刺期的合并带宽保护。v17 窗口批已 4/4 完成,该理由消失。维护者确认提前。
但先派只读审计,不直接开工
原单的方向 2(
field/schema收敛)已由 #3233 / PR #3296 在 v17 内落地,所以原单描述的病征至少有一半的前提已经过时:{...registeredProps},withFieldCarrier把schema解构消费掉再按引用转发field—— 原单头号症状schema="[object Object]"落到真实输入框,理论上已不复现;error,并接上生产者 (#3222) #3289 起的error="T is required"不在field与schema是同一个意思的两个载体,~25 个 widget 读field || schema#3233 的修复路径上;...props展开这个结构性问题原样存在,下一个声明 prop 仍会静默泄漏。在过时的病征描述上直接派 80 文件的机械改,是让 agent 对着一个不存在的症状写代码。先测量,再定界。
审计交付:jsdom 实测当前
origin/main上每个注册 widget 的真实 DOM 输出,列出仍在泄漏的属性与对应 widget,据此重写本单范围。审计只读、零文件冲突,与在飞的 rc.2 pin-bump PR 并行。裁决其余部分不变:路线 1(共享
toDomProps)+ 同 PR 必须带契约测试(渲染每个注册 widget,断言无未知小写属性落到 DOM),路线 3 否决。Generated by Claude Code
Generated by Claude Code
xuyushun441-sys commentedon Aug 3, 2026
审计结果 —— 本单三条前提被实测推翻,范围以本评论为准
只读审计已完成(
origin/main@91757a79f)。方法:真表单渲染器 + 真 widget,47 个注册 widget × 7 种 fixture 变体,外加独立的 SchemaRenderer(SDUI)走查;判定器在使用前做了双向验证(12 元素标准标记 fixture 上 0 误报,17 个植入的伪属性 17/17 检出)。审计未提交任何代码,工作树已清理。推翻的前提
1. 头号症状
schema="[object Object]"已不复现 —— 两条渲染路径上都归零。不是靠结果推断,是读代码确认了两道各自独立、任一道单独成立即可的封口:stripRegisteredFieldProps(form.tsx:275-289)解构掉schema,且form.tsx:1889-1904不再显式回传;withFieldCarrier(withFieldCarrier.tsx:69-71)消费schema只转发field—— 后者正是 SDUI 路径上唯一的防线。#3233 确实把它修掉了。2. 「~80 个 widget」是 14 个文件。 注册表实测共 47 个 field widget(
ComponentRegistry.getAllTypes()的field:命名空间),其中裸展开泄漏的 13 个,加上结构相同但目前被 Radix 吞掉的SelectField共 14 个文件,全在packages/fields/src/widgets/下。原估计比真实改动集大 5.7 倍,且超过了注册 widget 总数本身 —— 按 ~80 派单的人会去找 66 个不存在的文件。3. 真正的大头不是
error,是「作者字段配置的开放尾巴」。error影响 7 个 widget(text/number/date/datetime/time/password/boolean)。但作者在字段配置上写的任意额外键会全部落到 DOM,影响 13 个 —— 而且正是这条尾巴还在复现本单命名由来的那个[object Object]:机制:
form.tsx:1067-1085只解构固定的一组已知键,其余...fieldProps原样转发(:1385),而stripRegisteredFieldProps是按名删除、没有白名单。实测一个lookup字段上作者写的reference_to: 'contacts'会作为同名 prop 抵达 widget。因此:
toDomProps必须是白名单,不能是黑名单这是本次审计最有后果的一条输入。一个枚举当下渲染器专用键的黑名单(
schema/error/emptyHint/dataSource/dependentValues/dependsOn/options/inputType/ …)会放行本次审计的每一个 canary,也挡不住 SDUI 路径上的label。只有「保留已知安全键 +data-*+aria-*」能封住。好消息:这个白名单已经写好了 ——
packages/fields/src/widgets/types.ts的「DOM pass-through」段落已声明了确切集合(id/name/autoFocus/tabIndex/onBlur/onFocus/onClick+AriaAttributes+data-${string}索引签名)。toDomProps就是这段声明的运行时执行体,两者因此不会漂移,#3221 那个「封闭的类型但运行时可被违反」的论证也才第一次有了牙齿。本单未审视的一整条路径:SDUI 更宽
本单通篇只推理表单渲染器。实测 SchemaRenderer 路径泄漏严格更多 —— 多出
label,且作者节点上的任意键都会漏(SchemaRenderer.tsx:459-461把整个节点摊成 props,该路径没有stripRegisteredFieldProps等价物)。这条路径上 widget 自己的展开是唯一防线,这是白名单形状的第二个独立论据。契约测试:可行且便宜,但有四个坑
审计用的测量脚手架基本就是这个测试:47 widget × 7 变体 + SDUI 走查,light
dom项目下 ~17 秒跑完,连跑 4 次零 flake,不需要进heavyDomTests。落点packages/fields/src/__tests__/(widget-aria-invalid-e2e.test.tsx已有先例)。四个坑:label就是这么漏掉的)。required的boolean默认false能通过 react-hook-form 的 required 规则,于是错误从不产生、泄漏从不渲染 —— 第一轮因此把error泄漏数报成了 6,真值是 7。变体若静默没产生错误,测试必须失败而不是通过。vitest.config.mts)。happy-dom 的 IDL 有实测到的缺口(HTMLSelectElement无size、HTMLOptionElement无label),判定器需要一个极小的例外表并注明每条的理由。另需按前缀放行cmdk-*(第三方库自己盖在自己 DOM 上的标记,非 prop 透传)。实测结论:没有任何 widget 真的需要自定义属性 —— 47 个里一个都没有。声明的契约本来就只开放
data-${string}一族,所以「只有data-*是开放的」今天就可执行。定稿范围
SelectField.tsx),全在packages/fields/src/widgets/toDomProps助手(从packages/fields/src/index.tsx导出,理由同已导出的withFieldCarrier)+ 契约测试form.tsx不需要改 —— 实测它的 strip 层对它该负责的部分已经正确,剩余泄漏 100% 在 widget 侧。本单正文那句「+ 可能packages/components/src/renderers/form/」可以划掉,别去重开它顺带发现(另立单,不夹带进本单 PR)
field:select从不向辅助技术播报校验状态 ——SelectField把{...props}展开到 RadixSelect.Root(:113),Radix 静默丢弃,连aria-invalid/aria-describedby/aria-required一起丢。与 字段 widget 的错误提示键:spec 声明error,objectui 渲染errorMessage(declared ≠ enforced) #3222 修的是同一个失败类,select当时不在那批里。必须与本单排序(两者都动 SelectField 的展开点),不合并处理field:permission-facet-link未经withFieldCarrier注册(plugin-detail/src/index.tsx:459),是全仓唯一绕过field与schema是同一个意思的两个载体,~25 个 widget 读field || schema#3233 那道封口的 field 注册;SDUI 下会拿到schema而无fieldcapability-multiselect在实际路径上根本没注册,只有已废弃的registerFields()会注册它Generated by Claude Code
Generated by Claude Code
fix(fields): close the widget DOM prop leak with a whitelist `toDomPr…
fix(fields): objectstack-ai#3318 aria-invalid 交付批次 —— 20 个 widget 校验失…
fix(fields): group-labelled widget 的只读/零选项分支也消费 host label 的 IDREF (o…
test(app-shell): generalize the objectstack-ai#3291 DOM-leak canary s…
fix(fields): group-labelled widget 的只读/零选项面也消费 host 的 description IDR…
fix(components): 只读注册字段的替换显示接上 host 的命名与描述通道 (objectstack-ai#4788) (o…
docs(fields): tombstone MasterDetailField as a form-unreachable orphan
docs(fields): tombstone MasterDetailField as a form-unreachable orphan (