Found while implementing #5254 (the cross-namespace fallback removal). Out of that card's scope and not changed by its PR — recorded, not fixed there, per that card's ruling: "If some bare-name non-field component turns out to genuinely need to stay reachable from form fields, that returns as its own ruled card, not as a silent widening here."
Measured
On the built-in path (@object-ui/fields not registered), a form field spelled with the field:-prefixed id — which is exactly what mapFieldTypeToFormType emits for every object-derived form — resolves nothing and takes the builtin default input branch. That branch reads inputType, which an object-derived field does not carry, so it renders a plain text box:
get(field:password) = false
rendered type = text
attrs = ["class","id","aria-describedby","aria-invalid","type","name"]
For field:password that means the secret is on screen in clear text. For field:email it means the native keyboard/validation is lost.
Why this is NOT #5254
#5254 removed the bare-name fallback and gave the default branch a NATIVE_INPUT_FIELD_TYPES table so the bareemail / password spellings keep their native input. That table is keyed on the raw type, so the prefixed spelling misses it. Verified against origin/main before that change as well: the prefixed spelling already rendered type="text" there, because field:password never resolved and the default branch never read the field's declared type. So this is pre-existing, and #5254's PR neither introduces nor fixes it.
The question this needs decided, not guessed
Two readings, and they are not the same architecture:
- A — normalize in the default branch. Key the native-input table on
normalizeFieldType(type) so field:password and password resolve alike. One line, closes the safety hole on both spellings. - B — treat an unregistered
field: id as an authoring/registration error. A field:-prefixed type that resolves nothing means the app shipped without the widget it declares; rendering a silently degraded text box is the tolerance pattern AGENTS.md #0.1 warns about, and a loud failure (or a registration-time check) is the contract-first shape. Under this reading A hides the real bug.
The safety asymmetry argues for A being at least a stopgap; the contract argument argues that a form quietly rendering a password field as plain text is a symptom whose cause is elsewhere. Not derivable from #5254's ruling — leaving it to triage.
Starting points
Search record
search_issues run over the prefixed-field-type / plain-text-password / default-branch keywords; the hits were #5253 (closed, same branch, different defect), #2324 and #4220 (both closed, unrelated). No duplicate.
Found while implementing #5254 (the cross-namespace fallback removal). Out of that card's scope and not changed by its PR — recorded, not fixed there, per that card's ruling: "If some bare-name non-field component turns out to genuinely need to stay reachable from form fields, that returns as its own ruled card, not as a silent widening here."
Measured
On the built-in path (
@object-ui/fieldsnot registered), a form field spelled with thefield:-prefixed id — which is exactly whatmapFieldTypeToFormTypeemits for every object-derived form — resolves nothing and takes the builtindefaultinput branch. That branch readsinputType, which an object-derived field does not carry, so it renders a plain text box:For
field:passwordthat means the secret is on screen in clear text. Forfield:emailit means the native keyboard/validation is lost.Why this is NOT #5254
#5254 removed the bare-name fallback and gave the default branch a
NATIVE_INPUT_FIELD_TYPEStable so the bareemail/passwordspellings keep their native input. That table is keyed on the rawtype, so the prefixed spelling misses it. Verified againstorigin/mainbefore that change as well: the prefixed spelling already renderedtype="text"there, becausefield:passwordnever resolved and the default branch never read the field's declared type. So this is pre-existing, and #5254's PR neither introduces nor fixes it.The question this needs decided, not guessed
Two readings, and they are not the same architecture:
normalizeFieldType(type)sofield:passwordandpasswordresolve alike. One line, closes the safety hole on both spellings.field:id as an authoring/registration error. Afield:-prefixed type that resolves nothing means the app shipped without the widget it declares; rendering a silently degraded text box is the tolerance pattern AGENTS.md #0.1 warns about, and a loud failure (or a registration-time check) is the contract-first shape. Under this reading A hides the real bug.The safety asymmetry argues for A being at least a stopgap; the contract argument argues that a form quietly rendering a password field as plain text is a symptom whose cause is elsewhere. Not derivable from #5254's ruling — leaving it to triage.
Starting points
packages/components/src/renderers/form/form.tsx—renderFieldComponent, thedefaultswitch arm, andNATIVE_INPUT_FIELD_TYPESpackages/fields/src/field-type-alias.ts—mapFieldTypeToFormType, which emits the prefixed idstype: 'email'经 bare-name 回退落进 SDUI 节点渲染器,field(对象)与max_length一起泄漏到 DOM #5254 (the fallback removal), 内建 form 的default回退分支有和 #5201 完全相同的 ceiling 缺陷:max_length拿不到上限,只在 DOM 上留下失效属性 #5253 (the same branch's ceiling defect)Search record
search_issuesrun over the prefixed-field-type / plain-text-password / default-branch keywords; the hits were #5253 (closed, same branch, different defect), #2324 and #4220 (both closed, unrelated). No duplicate.