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
Filed by the domain:ui execution seat (PM session session_01CRJge11jso9TpXRWFt1Z49) from the measurement in #6765 / PR #6777. ⛔ This card does not repeat that measurement — #6765 is the authoritative record.
The measurement changed the question
#6765 predicted that browser sanitization would turn residue into an empty string. Measured in Chromium 141.0.7390.37 via Playwright, real widgets mounted from source, keyboard + real-clipboard paste + insertText + programmatic set:
Sanitization holds — but by FILTERING, not by emptying.
⇒ Residue never reaches these widgets, which makes #6715's anchored whole-string guard a provable no-op here — it would accept every string these boxes can produce and reject only strings the test environment fabricates. It was deliberately not copied.
The defect that WAS reproduced
Typing 1e leaves Chromium visibly displaying 1e while .value reads the empty string. So onChange receives null, aria-invalid stays "false", and nothing is said (screenshot taken). Pasting 1e into an empty box fires no change event at all, so the model silently keeps its previous value.
⚠️The truncating rows cannot be refused by ANY widget-side guard. Pasting 1.2.3 into a currency field stores 1.23; 0x10 stores 10. The information is already gone before handleChange runs — only abandoning type="number" recovers it.
Also now pinned: the oracle and the product disagree in 6 of 10 measured cases (e.g. 0x10 emits 0 under happy-dom and 10 in Chromium), so every existing unit test for these widgets drives a string no browser can deliver.
Options
A
Announce on e.target.validity.badInput across the whole class (Currency, Percent, Number, Geolocation), reusing #6716's refusal shape. ⭐ Uses the platform's OWN predicate rather than a renderer-side dialect — and badInput is the one signal happy-dom and Chromium were measured to AGREE on, so the oracle stops lying for exactly this guard. Needs an onBlur arm too (React fires nothing when the value stays empty); PercentField has no onBlur today. Leaves the filtering truncation silent.
B
Do nothing; document the drop as native type="number" behaviour and close. Zero risk, zero cost, honest given no user report exists. Leaves a money field that can display one thing and store another with nothing said.
C
Move to type="text" plus anchored parsing, exactly LocationField's shape. The only option that refuses both the silent drop and the filtering truncation, because it takes the text before the browser rewrites it. Cost: reverses #2572's deliberate min/max/step affordances, and loses the spinner and the mobile numeric keyboard on every currency and percent field in the product.
四维分析
① 实际业务需求。 静默丢弃是在钱字段上实测复现的,这是真的。⚠️ 但没有测到的是:有没有人真的往货币框里粘 1.2.3 或 0x10。所以 C 的高昂代价目前压在一条没有需求证据的路线上 —— 这与 #6715 的裁决拒绝做「度分秒/半球解析」是同一个理由。
② 平台长远合理性。 ⭐ A 是问平台它自己读到了什么,而不是再发明一套渲染器侧的「什么算数字」——contract-first。C 在架构上是最健全的终态,但它反转的是一个刻意做过的决定(#2572 的 min/max/step 与移动端数字键盘),那需要维护者,不是 dev。
③ 防 AI 写错元数据 —— 也是 A 唯一真正的风险,必须明说而不是掩饰。 沉默正是错值藏身之处,所以"说"胜过"不说"。⛔ 但:一个对 1e 报警、却对 1.2.3 静默存成 1.23 的控件,等于教会用户「没报警就说明值是对的」 —— 这正是 #6272 自己的原则反过来指向我们。所以 A 若实施,必须同时写明 filtering 截断仍然静默,否则它制造的信任比它消除的风险更贵。
④ 创业阶段不扩散需求。 A 是一个包内四个控件的有界改动;C 是四个控件重写外加产品里每一个数值字段的可用性回退,应当等到有实测用户报告再谈。
⭐ NumberField / GeolocationField were deliberately not filed as a separate card by the implementer, on the grounds that doing so would split one ruling across two cards. This seat agrees; that is why this card is scoped to all four.
Related
#6765 (the measurement) · PR #6777 (the comment-only records and the divergence pin) · #6716 (the silent-refusal class, ruled worth announcing for a coordinate) · #6715 / PR #6766 (the anchored guard, measurably a no-op here) · #6272 (the plausible-wrong-value precedent) · #2572 (the type="number" affordances C would reverse)
Filed by the
domain:uiexecution seat (PM sessionsession_01CRJge11jso9TpXRWFt1Z49) from the measurement in #6765 / PR #6777. ⛔ This card does not repeat that measurement — #6765 is the authoritative record.The measurement changed the question
#6765 predicted that browser sanitization would turn residue into an empty string. Measured in Chromium 141.0.7390.37 via Playwright, real widgets mounted from source, keyboard + real-clipboard paste +
insertText+ programmatic set:Sanitization holds — but by FILTERING, not by emptying.
12abc"""12"1.2.3"""1.23"0x10"""010"⇒ Residue never reaches these widgets, which makes #6715's anchored whole-string guard a provable no-op here — it would accept every string these boxes can produce and reject only strings the test environment fabricates. It was deliberately not copied.
The defect that WAS reproduced
Typing
1eleaves Chromium visibly displaying1ewhile.valuereads the empty string. SoonChangereceivesnull,aria-invalidstays"false", and nothing is said (screenshot taken). Pasting1einto an empty box fires no change event at all, so the model silently keeps its previous value.That is #6716's class — on a money field.
Two facts constrain any answer, and are why this was escalated rather than implemented:
NumberFieldandGeolocationFieldread the box the same way. This is one ruling about thetype="number"widget class.1.2.3into a currency field stores1.23;0x10stores10. The information is already gone beforehandleChangeruns — only abandoningtype="number"recovers it.Also now pinned: the oracle and the product disagree in 6 of 10 measured cases (e.g.
0x10emits0under happy-dom and10in Chromium), so every existing unit test for these widgets drives a string no browser can deliver.Options
e.target.validity.badInputacross the whole class (Currency, Percent, Number, Geolocation), reusing #6716's refusal shape. ⭐ Uses the platform's OWN predicate rather than a renderer-side dialect — andbadInputis the one signal happy-dom and Chromium were measured to AGREE on, so the oracle stops lying for exactly this guard. Needs anonBlurarm too (React fires nothing when the value stays empty);PercentFieldhas noonBlurtoday. Leaves the filtering truncation silent.type="number"behaviour and close. Zero risk, zero cost, honest given no user report exists. Leaves a money field that can display one thing and store another with nothing said.type="text"plus anchored parsing, exactlyLocationField's shape. The only option that refuses both the silent drop and the filtering truncation, because it takes the text before the browser rewrites it. Cost: reverses #2572's deliberate min/max/step affordances, and loses the spinner and the mobile numeric keyboard on every currency and percent field in the product.四维分析
① 实际业务需求。 静默丢弃是在钱字段上实测复现的,这是真的。⚠️ 但没有测到的是:有没有人真的往货币框里粘
1.2.3或0x10。所以 C 的高昂代价目前压在一条没有需求证据的路线上 —— 这与 #6715 的裁决拒绝做「度分秒/半球解析」是同一个理由。② 平台长远合理性。 ⭐ A 是问平台它自己读到了什么,而不是再发明一套渲染器侧的「什么算数字」——contract-first。C 在架构上是最健全的终态,但它反转的是一个刻意做过的决定(#2572 的 min/max/step 与移动端数字键盘),那需要维护者,不是 dev。
③ 防 AI 写错元数据 —— 也是 A 唯一真正的风险,必须明说而不是掩饰。 沉默正是错值藏身之处,所以"说"胜过"不说"。⛔ 但:一个对
1e报警、却对1.2.3静默存成 1.23 的控件,等于教会用户「没报警就说明值是对的」 —— 这正是 #6272 自己的原则反过来指向我们。所以 A 若实施,必须同时写明 filtering 截断仍然静默,否则它制造的信任比它消除的风险更贵。④ 创业阶段不扩散需求。 A 是一个包内四个控件的有界改动;C 是四个控件重写外加产品里每一个数值字段的可用性回退,应当等到有实测用户报告再谈。
本席意见与实施席一致:A,但范围按
type="number"这一类的四个控件立成一张卡,⛔ 不是给 #6765 点名的那两个打补丁;且与「截断仍静默」的书面说明同批发布。⛔ 但这改变已发布控件在什么条件下向用户发声,且要在
PercentField上新增onBlur面,属于公开行为契约变更,落在人工地板上 —— 本席记录,不代裁。Scope note
⭐
NumberField/GeolocationFieldwere deliberately not filed as a separate card by the implementer, on the grounds that doing so would split one ruling across two cards. This seat agrees; that is why this card is scoped to all four.Related
#6765 (the measurement) · PR #6777 (the comment-only records and the divergence pin) · #6716 (the silent-refusal class, ruled worth announcing for a coordinate) · #6715 / PR #6766 (the anchored guard, measurably a no-op here) · #6272 (the plausible-wrong-value precedent) · #2572 (the
type="number"affordances C would reverse)