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.

[plugin-form] fullscreenLongText 盖章面收裸 textarea 却不收裸 markdown/html/richtext,而这三个裸拼法照样解析到 RichTextField #4838

Closed

Description

@yinlianghui

做 #4831 时顺带测到,按 Prime Directive #10 立单(不认领、不扩围)。与 #4831 不是同一条:#4831 修的是 widget id field:richtext 漏出盖章面;本条谈的是同一份清单在另一条输入路径上的不对称,而且它牵一个契约问题,不是补字面量能了结的,故单独记录。

事实

ObjectForm 的盖章清单(#4831 之后)是五个字面量 —— packages/plugin-form/src/ObjectForm.tsx:1163-1166:

const t = f.type as string | undefined;
const isTextarea = t === 'textarea' || t === 'field:textarea' ||
  t === 'field:markdown' || t === 'field:html' ||
  t === 'field:richtext';

裸拼法只收了 textarea 一个。它在这里的理由是明确的:textarea 是表单渲染器的内建分支(BUILTIN_FIELD_TYPES,packages/components/src/renderers/form/form.tsx:235 = input/textarea/checkbox/switch/select),那条 case 'textarea'(同文件 :2493)自己读 mobile_fullscreen。

而 markdown / html / richtext 三个裸拼法不在 BUILTIN_FIELD_TYPES 里、也不含冒号,于是走 renderFieldComponent 的命名空间兜底 —— 同文件 :2427-2429:

const namespacedWidget = !BUILTIN_FIELD_TYPES.has(type) && !type.includes(':')
  ? ComponentRegistry.get('field:' + type)
  : undefined;

field:markdown / field:html / field:richtext 三个键都注册到 RichTextField(packages/fields/src/index.tsx:2411-2413),读者也在(RichTextField.tsx:175)。于是:一个 customFields: [{ name: 'x', type: 'markdown' }] 的字段会渲染出 RichTextField,却拿不到 mobile_fullscreen,没有展开按钮 —— 与 #4831 修掉的那一半形状相同,只是入口是裸拼法而不是 widget id。

为什么没有并进 #4831 一起改

因为它导出两个方向完全不同的修法,取决于一个未定的契约问题:裸的 spec 类型名到底算不算 FormField.type 的合法拼法?

  • 支持「算」的一侧:同包的孪生集合 WIDE_FIELD_TYPES(packages/plugin-form/src/autoLayout.ts:57-68)故意收两种拼法,其 TSDoc 的理由是 app-shell 的 ObjectFormDesigner 把 objectDef.fields[x].type 原样传进来 —— 实测确有该调用(packages/app-shell/src/views/studio-design/ObjectFormDesigner.tsx:158)。
  • 支持「不算」的一侧:AGENTS.md #0.1 契约优先 —— renderFieldComponent 的裸名兜底本身就是一个宽容消费者,正确修法可能是在生产者/发布期拒绝裸拼法,而不是让盖章面再跟着宽容一次。

一个方向是补三个字面量,另一个方向是收紧兜底;#4831 只修 widget id 那一半,这条原样交出来。

影响面(未夸大)

能否被今天的真实 metadata 命中,取决于上面那个契约问题的答案。未实测到有生产者向 ObjectForm.customFields 写裸 markdown/html/richtext(designer 那条走的是 isWideFieldType,不是盖章面)。因此按 observation-class 归档、打 finding、不挂 pm:queue,交 PM 定级。

Activity

  1. os-support-ai commented on Aug 18, 2026

    @os-support-ai
    Collaborator

    Triage — first-touch grading (triage seat, session session_01KTguWpd7WNGYTGmSNY31RL): escalated to needs-user-decision, type Bug.

    The card is honest that the two fixes point in opposite directions and hinge on one undecided contract question: is a bare spec type name (markdown/html/richtext) a legal FormField.type spelling? That is contract shape, not mechanics — and both sides have real evidence: WIDE_FIELD_TYPES tolerates both spellings because ObjectFormDesigner.tsx:158 passes objectDef.fields[x].type raw (a real producer of bare spellings), while the namespaced fallback in renderFieldComponent is precisely the kind of tolerant consumer the contract-first rule says to tighten, not extend.

    Four-prong analysis:

    • Platform long-term coherence: one legal spelling per widget beats two; every tolerant consumer added around the bare spelling (this stamp list would be the next) entrenches the fork further.
    • Measured business pull: zero on the defect face today — no measured producer writes bare markdown/html/richtext into ObjectForm.customFields; the designer path routes through isWideFieldType, not the stamp face.
    • AI-agent error-resistance: a closed spelling set rejected loudly at publish beats each consumer maintaining its own tolerance list — the asymmetry this card records is exactly the drift the doubled vocabulary produces.
    • Startup scope discipline: the 3-literal patch is the cheap move but is a recurring tax (every future stamp-like list must remember both spellings); ruling the spelling contract once ends the class.

    Recommendation: rule bare spellings not legal — normalize the producer (designer emits the namespaced/spec-typed form) and schedule the fallback tightening; do not extend the stamp list to bare spellings in the interim (that widens the tolerance being retired). If you instead rule bare spellings legal, the fix is the 3 literals plus a shared spelling-normalization helper so no list keeps its own copy.


    Generated by Claude Code

  2. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions