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.
aria-labelledby 落在无 role 的 span / div 上命名的是 NOTHING —— generic 禁止作者名。这不是我的判断,是本仓 toHostGroupProps.ts 里 #3990 已经写下并被接受的结论(「so the pair is inert without a role that supports naming」),也正是那一族当初引入 role="group" 的唯一理由。
只把 host id 放到替换元素上而 label 侧不动,for 从 DANGLING 变成 RESOLVES-但-不可-label(#3961 修的正是这个形态)。关联依旧断,但「悬空 for」这个可扫描信号没了 —— #3990 原话:「a for that resolves to nothing can at least be swept for」。所以 widget 侧单边落地比不动更坏。
Maintainer ruling (2026-08-16, recorded by PM session session_01NYgmGheCzM6NrHZN436Cxf). The maintainer accepted the evening decision-inbox report's recommendations in full, verbatim: 「8931 我已经处理了,其他接受你的所有建议。」 The dev + PM recommendations (A / A / E) are ratified on all three questions:
Mechanism lands in form.tsx, host-side, one wrapper (Option E): when readonly resolves to a registered field widget, the host wraps the widget output in a container carrying id + aria-labelledby (composite "labelId hostId" on anchor faces so the value stays in the accessible name) + aria-describedby + role="group", with aria-invalid stripped per the [fields] 只读态的 group-labelled 字段:description 的 IDREF 同样没有消费者(#3990 修的是「名」,这条是「描述」) #4005 boundary. All 32 current widgets and any future third-party widget are correct by construction — no "remember to spread" entry point. The extra DOM layer per readonly field is an accepted cost.
Scope: the D-group display-only four (Formula / Summary / AutoNumber / Vector) are in the same batch, per the disposition. The earlier pre-ruling in comment 5306507639 stands withdrawn (falsified by measurement), as recorded.
Label flipped needs-user-decision → pm:queue in the same stroke.
Activity
yinlianghui commentedon Aug 16, 2026
【PM 分诊】座位
repo:objectui(session_01GTRjn8xBqp75dk7kFupVRt):晋级pm:queue—— email/url/phone 只读替换显示丢整份 host plumbing(id 无宿主、label for 悬空、链接在无障碍树无名),比 #4005 重一档,PR #4791 实测在案。机制方向预裁(留否决窗):不照搬 toHostGroupProps(正文判断正确:锚非 labelable、role=group 不对)—— 但
aria-labelledby/aria-describedby是全局 ARIA 属性,link 是合法命名/描述承载:只读替换元素接 hostid+aria-labelledby指向 label(label 侧随之去 for 或保 for 并存皆量后定,单一命名通道纪律照 #3978)+aria-describedby指向 description;role 保持元素原生(link 等),⛔ 不发 group。这是 #3961/#3990/#4005 家族同一 IDREF 机制在非 group 面的直接延伸。派发前置:正文「未实测」段照办 —— 32 个带 readonly 提前 return 的 widget 不按三行外推,实施者先量本单三类 + 逐个扫描形态分组回传(同形一并修、异形回传)。Generated by Claude Code
yinlianghui commentedon Aug 16, 2026
【认领】objectui 分片 PM(session_01GTRjn8xBqp75dk7kFupVRt)批次 11 派发,os-dev 实施。分支:
claude/issue-4788-readonly-replacement-plumbing。按 2026-08-16 预裁评论的机制方向执行(全局 ARIA 属性 + host id,role 保持原生,⛔ 不发 group);先修实测在案的 email/url/phone 三个,再按正文「未实测」段逐个扫描 32 个 readonly 提前 return 的 widget 形态分组 —— 同形一并修、异形回传不外推。文件面:packages/fields/src/widgets/**+ 对应测试(与在队 PR #4789 的 index.tsx/core 改动无交集)。Generated by Claude Code
yinlianghui commentedon Aug 16, 2026
【os-dev 实测回报 —— 前提成立且更宽,但预裁的机制方向有两处被实测否决,停手待裁】
worktree
claude/issue-4788-readonly-replacement-plumbing,base8a9deceea。probe 与 #3990 / #4005 同一套(真 form renderer + 裸注册,字段带description: 'Some help'),跑完即删、未入仓。未提交任何实现代码。1. 前提成立,而且比正文更宽:32 个全部同形
正文「有的只读仍渲染真控件(那就没有这条)」这一假设被证伪。逐个量下来,32 个非 group-labelled widget 的只读态无一例外:
只读面形态分三类,但缺陷完全一致:锚 3 个(email / phone / url-合法)、纯文本/容器 26 个(span / div / pre / p / table)、display-only 3 个(formula / summary / auto_number / vector —— 无可编辑分支,整个 widget 就是替换显示)。
2. 否决点一:控件通道的 host label 根本没有 id,
aria-labelledby无处可指预裁方向写的是「只读替换元素接 host id +
aria-labelledby指向 label」。实测 32 行全部labelId=NONE:groupLabelId只在resolveFieldLabelling() === 'group'时生成(form.tsx),控件通道的FormLabel只有htmlFor,自身不发 id。所以「指向 label」的 IDREF 目标今天不存在 —— 这一半无法在packages/fields/src/widgets/**内实现,必须由packages/components/.../form/form.tsx先发布 label id(即预裁里那句「label 侧随之去 for」,但它是前置条件而不是后续动作)。3. 否决点二:「role 保持元素原生」对 26/32 是惰性的,而且测试层看不见这条禁令
aria-labelledby落在无 role 的 span / div 上命名的是 NOTHING ——generic禁止作者名。这不是我的判断,是本仓toHostGroupProps.ts里 #3990 已经写下并被接受的结论(「so the pair is inert without a role that supports naming」),也正是那一族当初引入role="group"的唯一理由。更要命的是测试层判不出这件事。实测:
即:照预裁方向实施,26 个 span/div/pre 面会得到测试全绿、AT 完全惰性的幽灵修复,并且还会配一条钉子把它认证下来。这正是本仓视为一等失败的「declared but not delivered」形态(#3290 / #3222 家族),所以我没有推。
4. 读数三:锚元素上「只指向 label」是信息回退
email/url/phone 的只读锚今天的可访问名是内容(
user@example.com)。给它aria-labelledby只指向 label,名字变成Email—— 值从可访问名里消失了。复合aria-labelledby="labelId hostId"才得到Email user@example.com(上表第三行实测)。这是一个需要明裁的取舍,不是实现细节。5. 另一条必须一起裁的:只放 host id 会掩盖现有信号
只把 host
id放到替换元素上而 label 侧不动,for从 DANGLING 变成 RESOLVES-但-不可-label(#3961 修的正是这个形态)。关联依旧断,但「悬空 for」这个可扫描信号没了 —— #3990 原话:「aforthat resolves to nothing can at least be swept for」。所以 widget 侧单边落地比不动更坏。6. 选项与建议(两轴分析)
A. 照预裁原样(widget 侧 host id + aria-labelledby 指向 label + aria-describedby,role 保持原生) —— 26/32 惰性且测试绿(§3),3 个锚信息回退(§4)。长期:把一条「声明了没送达」的关联固化进 32 个 widget 并配钉子认证。AI 编写面:最差 —— 教出来的范式正是那种静默失效形状。不建议。
B. 家族机制外推(host 发 label id + 去 for;widget 只读分支按面形态取 role) —— 纯文本面走
toHostGroupProps(props, 'instead-of-the-inputs')(含role="group"),锚保持role=link并用复合aria-labelledby="labelId hostId"保住值。长期:与 [fields] 复合/分组 field widget 丢弃或错投 host 下发的控件 id:address / geolocation 的表单 labelfor悬空,checkboxes / radio / rating / file 的for落在不可 label 的 div 上 #3961/[fields] 只读 / 零选项的 group-labelled 选项 widget 提前 return,groupDomProps没被展开 —— host label 的 IDREF 没有消费者,标签仍然什么都不命名(#3961 的残留,checkboxes 今天的 main 上就这样) #3990/[fields] 只读态的 group-labelled 字段:description 的 IDREF 同样没有消费者(#3990 修的是「名」,这条是「描述」) #4005 同一机制、同一 helper;对第三方 widget 优雅降级(只读分支真渲染了控件,也只是从for改成 IDREF 命名,仍是单通道)。代价:改 host 契约 + 32 个文件,且要接受「单值显示也叫 group」这一语义主张([fields] 只读 / 零选项的 group-labelled 选项 widget 提前 return,groupDomProps没被展开 —— host label 的 IDREF 没有消费者,标签仍然什么都不命名(#3961 的残留,checkboxes 今天的 main 上就这样) #3990 已为 7 个复合面接受过)。C. 显式声明(
ComponentMeta.labelling增加 readonly 维度) —— 最契约优先,不靠「registered + readonly」推断;代价最高,且漏声明会静默退回今天的坏形态。D. 只读也保留真可 label 控件(内建路径今天的做法,实测
for=RESOLVES-LABELABLE) —— 唯一让这类缺陷结构上不可能发生的选项,但它表达不了 mailto/tel/徽章/图片这些替换面,且是产品外观变更。不建议作为整体解。E(建议). 由 host 包一层,widget 一个文件都不改 —— form.tsx 在「readonly 且解析到注册 field widget」时,把 widget 渲染结果包进一个承载
id+aria-labelledby(label 发 id、去 for)+aria-describedby+role="group"的容器(FormControl的 Slot 自然把 id/aria-describedby 注到这层;aria-invalid按 [fields] 只读态的 group-labelled 字段:description 的 IDREF 同样没有消费者(#3990 修的是「名」,这条是「描述」) #4005 的边界剥掉)。两轴:长期健全 —— 一处机制,当下 32 个和将来任意第三方 widget 一律正确,不存在「下一个作者忘了 spread」的入口;锚保持
role=link、名字仍来自内容,§4 的取舍自动消失(进组时读组名,组内读链接)。让 AI 写的代码难写错 —— 最优:结构上不可能忘,因为 widget 侧无事可做。代价:每个只读注册字段多一层 DOM(CSS / e2e 选择器的爆炸半径最大),且同样要接受role="group"的语义主张。建议 E,退而求其次 B;明确不建议 A。 两者都要维护者先裁三件事:(1) 只读控件通道的 label 是否去
for改发 id(实测答案:必须,否则命名不可能,且单一命名通道纪律 #3978 也要求二选一);(2) 只读值显示是否接受role="group"(预裁 ⛔ 了它,但实测 §3 表明不给 role 就等于不修);(3) 修在 host 一处(E)还是 32 个 widget(B)。裁完我可以一轮做完。附:上面每一行读数都可复跑,probe 已删未入仓。
Generated by Claude Code
yinlianghui commentedon Aug 16, 2026
【PM 处置 · 转决策箱】objectui 分片 PM(session_01GTRjn8xBqp75dk7kFupVRt):
pm:queue→needs-user-decision。dev 零代码停手正确 —— PM 2026-08-16 预裁的机制方向被实测两点证伪(①控件通道 host label 32/32 不发 id,aria-labelledby 无 IDREF 目标,widgets 包内无法独立实现;②span/div 面 role 保持原生则命名在 ARIA 上惰性,且 jsdom 测试对此假绿 —— dev 用受控实验证明了这条幽灵修复路径),该预裁撤回。完整读数与选项分析见 dev 评论。三问待裁(dev 建议 A / A / E,PM 附议):
for改发 id(A,单一命名通道纪律)vs 双通道并存(B);role="group"(A,与 [fields] 只读 / 零选项的 group-labelled 选项 widget 提前 return,groupDomProps没被展开 —— host label 的 IDREF 没有消费者,标签仍然什么都不命名(#3961 的残留,checkboxes 今天的 main 上就这样) #3990 七复合面同一主张、同一 helper;PM 原 ⛔ 基于错误前提撤回)vs 坚持原生(B,已被实测排除:测试绿而 AT 惰性)vs term/definition(C,语义主张更大);范围补充:D 组 display-only 四件(Formula/Summary/AutoNumber/Vector)同缺陷,任何裁决应算进同一批;C 组两件不可达已另行处置(#4811)。裁定后一轮可做完 —— 形态分组、probe 方法、实施路径已备。
Generated by Claude Code
os-zhuang commentedon Aug 16, 2026
Maintainer ruling (2026-08-16, recorded by PM session
session_01NYgmGheCzM6NrHZN436Cxf). The maintainer accepted the evening decision-inbox report's recommendations in full, verbatim: 「8931 我已经处理了,其他接受你的所有建议。」 The dev + PM recommendations (A / A / E) are ratified on all three questions:forand emits anid— the single-naming-channel discipline (fix(fields,components): 复合/分组 field widget 的组标签改用 IDREF 关联(#3961) #3978); without a label id there is no IDREF target, which the measurement proved is the precondition, not a follow-up.role="group"is accepted on readonly value surfaces — the same semantic claim [fields] 只读 / 零选项的 group-labelled 选项 widget 提前 return,groupDomProps没被展开 —— host label 的 IDREF 没有消费者,标签仍然什么都不命名(#3961 的残留,checkboxes 今天的 main 上就这样) #3990 already established for seven composite surfaces, and the measurement showed that "keep the native role" on span/div faces yields naming that is inert to AT while tests stay green (the ghost-fix path is rejected on the record).form.tsx, host-side, one wrapper (Option E): when readonly resolves to a registered field widget, the host wraps the widget output in a container carryingid+aria-labelledby(composite"labelId hostId"on anchor faces so the value stays in the accessible name) +aria-describedby+role="group", witharia-invalidstripped per the [fields] 只读态的 group-labelled 字段:description 的 IDREF 同样没有消费者(#3990 修的是「名」,这条是「描述」) #4005 boundary. All 32 current widgets and any future third-party widget are correct by construction — no "remember to spread" entry point. The extra DOM layer per readonly field is an accepted cost.Scope: the D-group display-only four (Formula / Summary / AutoNumber / Vector) are in the same batch, per the disposition. The earlier pre-ruling in comment 5306507639 stands withdrawn (falsified by measurement), as recorded.
Label flipped
needs-user-decision→pm:queuein the same stroke.Generated by Claude Code
yinlianghui commentedon Aug 16, 2026
【认领】objectui 分片 PM(session_01GTRjn8xBqp75dk7kFupVRt)批次 12 派发,os-dev 实施。分支:
claude/issue-4788-readonly-host-wrapper。按 2026-08-16 维护者裁定(A / A / E,评论 5307576062)执行:host label 去
for改发 id;只读值面接受role="group";机制落packages/components/.../form/form.tsxhost 一处包容器(id+aria-labelledby,锚面复合"labelId hostId"保值 +aria-describedby+role="group",aria-invalid按 #4005 边界剥离);D 组 display-only 四件(Formula/Summary/AutoNumber/Vector)同批。文件面:⚠️ 在飞互斥声明:#4824+#4832(同批次另一 dev)也要动 form.tsx 的内置 FullscreenTextarea 段,已给对方挂「本 PR 落 main 后才动 form.tsx」前置门。
packages/components/src/renderers/form/form.tsx+ 对应测试与钉子;⛔packages/fields/src/widgets/**零改动(E 方案的结构卖点,验收按此对账)。Generated by Claude Code
test(fields): pin the readonly group's NAME, not its text content (#4788
yinlianghui commentedon Aug 16, 2026
【os-dev 实施完成 —— draft PR #4862】分支
claude/issue-4788-readonly-host-wrapper,base1ef236e18。按 2026-08-16 维护者裁定(A / A / E,评论 5307576062)三条逐条落实:只读态 host label 去
for改发 id;只读值面role="group";机制落packages/components/src/renderers/form/form.tsx一处包容器,aria-invalid按 #4005 边界剥离。packages/fields/src/widgets/下零改动(验收可按此对账:PR 改动文件共 4 个 —— form.tsx、两个新测试、一个 changeset)。前提复核:成立,读数与测量评论一致
在
origin/main1ef236e18上复跑同一 probe。34 个注册的非 group-labelled 类型(含 D 组四件)只读行全部for=DANGLING labelId=NONE hostIdEl=NONE consumers=0;修复后全部变为for=NO-FOR labelId=SET hostIdEl=div[group] consumers=1 ariaInvalid=(absent),可访问名形如Label email user@example.com。36 行 editable 读数改动前后 diff 为空。一处与测量评论表述的细微出入,如实记录:评论里的「32 个」是按 widget 源文件数,我按注册类型枚举得 34 个非 group-labelled 类型(
markdown/html/richtext共用 RichTextField、user/owner共用 UserField、lookup/master_detail共用 LookupField);另外textarea/select的裸拼写是BUILTIN_FIELD_TYPES成员,不查注册表,只读态照旧渲染真控件(实测consumers=1),所以测的是它们的field:限定拼写。缺陷结论不变。抽样(裁定要求的四类)
Label email user@example.com;锚自身保持role=link、名字仍来自内容user@example.com,未被加第二个aria-labelledby;role="group"+ 复合命名,不是无 role span 的惰性对;toHaveAccessibleDescription('Some help')绿。钉子测试按要求写的是容器归属(每个 IDREF 用
getElementById解析、断言解析到的节点是正确 form item 内的正确元素),不是单靠toHaveAccessibleName。反向验证(方向先判后跑)
预判红(非反转),逐条点名 8 + 112 条应红、5 + 4 条应绿;摘掉包容器逻辑后实跑
Tests 120 failed | 9 passed (129),8 条名字与预判完全相同。git checkout还原,未用 stash。验证读数
vitest run packages/components packages/fields packages/plugin-form→ 284 files / 3362 tests 全绿;turbo run type-check(components + fields)→ 12/12 successful;check-control-bytesOK;三个 changeset 门 OK。范围外(已另立单,未塞进本 PR)
#4857 —— 七个 widget(formula / summary / auto_number / vector / grid / slider / signature)在可编辑态也丢 host id,
for同样悬空。本裁定的 host 门是readonly,而packages/plugin-form/src/ObjectForm.tsx:687把计算型字段映射成disabled而非readonly,所以 D 组在真实 ObjectForm 里仍绕开本 PR 的容器。该条需要一次独立的方向裁决(改映射 / 加 display-only 声明 / widget 侧展开),不宜按本单外推。Generated by Claude Code
fix(components): the built-in textarea's fullscreen dialog announces …
fix(components): 只读注册字段的替换显示接上 host 的命名与描述通道 (objectstack-ai#4788) (o…
fix(components,fields): 全屏长文本对话框播报校验状态、控件拿到可访问名 (objectstack-ai#4824, o…
docs(fields): tombstone MasterDetailField as a form-unreachable orphan
feat(core,components,fields): labelling grows 'display'; display four…
fix(app-shell): SchemaForm 的六条非注册表渲染路径按解析出的面发命名通道 (objectstack-ai#5039)…
fix(app-shell): metadata-admin 嵌套字段 id 按路径作用域 + grid/table 单元格由列头命名 (o…
docs(fields): tombstone MasterDetailField as a form-unreachable orphan (