Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

[app-shell] metadata-admin SchemaForm 的 6 条非注册表渲染路径仍发悬空 for(composite / repeater / record / 两条结构化兜底 / raw-JSON) #5039

Closed

Description

@yinlianghui

发现于 #4871 的账本实测(PR 分支 claude/issue-4871-schemaform-labelling,基线 167ec42e7)。未认领,交 PM triage。

机制

#4871 给 WIDGETS 注册表加了 labelling 声明层,MetadataField 据此发 for 或 IDREF。但 FieldControl 里有 6 条不经过注册表的渲染路径 —— 它们没有注册键,因而没有声明面,MetadataField 一律按默认的 control 发 for,而这 6 条路径全都不消费 id。

同一次 probe(真 SchemaForm 渲染,可编辑 + 只读两态,列口径与 #4871 账本一致)测得:

                          editable                        read-only
(spec) composite          for=DANGLING hostIdEl=NONE      for=DANGLING hostIdEl=NONE
(spec) repeater           for=DANGLING hostIdEl=NONE      for=DANGLING hostIdEl=NONE
(spec) record             for=DANGLING hostIdEl=NONE      for=DANGLING hostIdEl=NONE
(fallback) nested-object  for=DANGLING hostIdEl=NONE      for=DANGLING hostIdEl=NONE
(fallback) array-of-obj   for=DANGLING hostIdEl=NONE      for=DANGLING hostIdEl=NONE
(fallback) raw-json       for=DANGLING hostIdEl=NONE      for=DANGLING hostIdEl=NONE

对照:同一次 probe 里的内建标量分支(string / number / enum-select / array-of-primitive / boolean switch)全部 for=RESOLVES-LABELABLE,不在本单范围。

hostIdEl=NONE 即文档里没有任何元素持有 mdf-<字段名> —— 与 #4871 / #4857 / #4788 同一失效类:可见 label 的关联「解析到无」,工具链读作已闭合的关联,比干脆没有关联更糟。

为什么 #4871 没有顺手做

#4871 的合裁(2026-08-17)明确把门定在注册表上:「registered ⇒ declared」。这 6 条路径没有注册键,声明层无处挂;更要紧的是决定权的位置:

所以按 Prime Directive #10 单独立卡,不夹带进 #4871 的 PR。

建议

两种落地形态,成本差别明显,建议由维护者裁:

  1. 上提解析:把 FieldControl 里的 union 解析 + 结构化分支判定抽成一个纯函数,MetadataField 先调它拿到「这个字段最终渲染 control 还是 group」,再据此发通道。与 [app-shell] metadata-admin 的 SchemaForm 无条件给每个 widget 发 Label htmlFor={id},但 17 个注册 widget 里只有 5 个消费 id —— 分组类控件面的 for 悬空且组无可访问名 #4871 的 resolveRegisteredWidget 同形,契约一致;改动集中在 SchemaForm.tsx。
  2. 宿主包裹:对这 6 条路径套 [fields] 非 group-labelled widget 的只读替换显示丢掉 host 下发的整份 plumbing —— label 的 for 悬空、description 零消费者(email / url / phone 实测) #4788 的宿主容器(id + aria-labelledby + role="group"),label 去掉 for。不需要上提解析(6 条路径都是「非可 label 面」这一类),但要先确认第 3 点里那三条兜底不会退化到某个可 label 的标量控件。

边界与去重

Activity

  1. self-assigned this
    on Aug 17, 2026
  2. yinlianghui commented on Aug 17, 2026

    @yinlianghui
    ContributorAuthor

    认领(objectui 分片 PM 直辖,批次 22 补席)—— session session_01GTRjn8xBqp75dk7kFupVRt,分支 claude/issue-5039-nonregistry-labelling。os-dev 席位实施,PM 只读验收。前置已解除:PR #5041(#4871 注册表半)已落 main dc9d651a2,同文件互斥消失。

    PM 代裁(留否决窗):采卡面方案 1(上提解析)。这不是新的机制选型,而是 05:03Z 维护者合裁在同一宿主的机械延伸 —— 该合裁裁掉的正是「label 已写完才决定渲染什么」的运行期推断,#4871 以 resolveRegisteredWidget()(写 label 前解析)实施于注册表 19 项;本单 6 条非注册表路径照同一形状把 FieldControl 内的 union 解析 + 结构化分支判定抽成纯函数、上提到 FieldRow 写 label 之前。弃方案 2 的理由:它保留晚判定、只是把 6 条路径全包成 group,且带一个未验证前提(三条兜底永不退化到可 label 标量面)—— 方案 1 无此前提,解析函数本身就是判据。维护者对此代裁有异议可在本单或 PR 否决,翻转成本已知(两形态都在卡面)。

    实施钉子:① 账本先行 —— 在基线复现 6×2 的 DANGLING/hostIdEl=NONE 读数;② 通道按解析结果发:probe 读数显示这 6 条全是非可 label 面 ⇒ group 通道(IDREF、摘 for、不下发 id,#4788 容器形),若解析函数发现某路径实际落到可 label 标量面则该路径走 control —— 判据在函数,不在硬编码清单;③ ⛔ 不动 PR5041 落地的 WIDGET_LABELLING 声明表与 52 例断言(全量回归须绿);④ 测试照 SchemaForm.widgetLabelling.test.tsx 的正向断言形(具名面存在并应答 IDREF),6 路径 × 两态。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpm:queue

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions