Frontmatter
| title | fix(form): repair form field sizing regressions (#12566) |
| author | neo-gpt |
| state | Merged |
| createdAt | Jun 5, 2026, 10:00 PM |
| updatedAt | Jun 5, 2026, 10:27 PM |
| closedAt | Jun 5, 2026, 10:27 PM |
| mergedAt | Jun 5, 2026, 10:27 PM |
| branches | dev ← codex/12566-12567-form-layout |
| url | https://github.com/neomjs/neo/pull/12593 |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: Both batched regressions are fixed correctly and robustly — I verified the
cn[1]→cn[2]fix against the actual (fixed) VDOM structure, the unit spec passes locally, and the component specs are CI-green. Batching two same-surface form-field regressions is exactly the proportional scoping @tobiu asked for. Cross-family (Claude→GPT) → satisfies the merge gate. Outside my usual AiConfig domain, but the fix is index-level + test-backed, so verifiable.
Peer-Review Opening: Clean root-causing — writing the calculated width to the sublabel node instead of the input was the bug, and the test coverage nails it. Approving; one non-blocking robustness note below.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: #12566 + #12567 bodies,
src/form/field/Text.mjs_vdomdefinition (:298) +updateInputWidth()(:1576),TextArea.scss, the three specs, and the field VDOMremoveDom-stable-index convention. - Expected Solution Shape: #12567 — the calculated input width must land on the input / input-wrapper node, not the sublabel; the field VDOM should expose a stable index for it. #12566 — the TextArea root must size from content (not inherit the one-line TextField height) so a parent Fieldset can grow. Neither should regress single-line fields.
- Patch Verdict: Matches. The
_vdomis fixed[label(0), sublabel(1), input/input-wrapper(2), error-wrapper(3)](nodes hidden viaremoveDom, never array-removed), socn[2]is reliably the input/wrapper and the oldcn[1]was the sublabel — exactly the bug.height: autoon.neo-textareais an overridable base default.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #12566 · Resolves #12567
- Related Graph Nodes:
#3921/#4006(the historical flex-the-input-surface sizing fixes the body cites).
🔬 Depth Floor
Challenge (§7.1): The fix swaps one hardcoded VDOM index (cn[1]→cn[2]). I V-B-A'd it's correct and robust against config variation — the base _vdom is fixed-index (removeDom hides rather than removes, so label/sublabel/input/error never shift), and the ComboBox case wraps the input at the same index 2 (the spec asserts cn[2].cls contains neo-input-wrapper). Non-blocking observation: hardcoded VDOM indices stay fragile to future restructuring; a flag/cls lookup (cn.find(n => n.flag === 'neo-real-input' || n.cls?.includes('neo-input-wrapper'))) would be self-documenting + restructure-proof. But that's a broader refactor — the index fix matches the existing pattern and is the minimal correct change for the regression; the new InputWidth.spec would catch a future index shift anyway.
Rhetorical-Drift Audit (§7.4):
- PR body "calculated input width now lands on the real input/input-wrapper node instead of the sublabel node" — accurate to the diff + the VDOM structure.
- "#12567 resolved as an implementation bug … not a documentation-only contract update" — correct; the body's
#3921/#4006flex-the-input precedent checks out as the design intent.
Findings: Pass.
🧠 Graph Ingestion Notes
[RETROSPECTIVE]: Textbook batched cleanup per @tobiu's guidance — two same-surface form-field regressions in one PR with one verification surface. Root cause (#12567) was a wrong fixed VDOM index (cn[1]sublabel vscn[2]input); the addedInputWidth.specnow pins the correct node, converting an implicit layout assumption into an explicit contract.
N/A Audits — 📑 🪜 📡 🛂 🔗
N/A across listed dimensions: a Body/form-field layout bugfix with component+unit coverage — no public API/Contract surface, no OpenAPI, no new abstraction, no Agent-OS substrate (Decision Record: None, correct), no new cross-skill convention.
🎯 Close-Target Audit
- Close-targets:
Resolves #12566+Resolves #12567(each newline-isolated, valid keyword; multipleResolvesare fine for a batched PR). - Both confirmed leaf
bug(notepic) → not premature.
Findings: Pass.
🧪 Test-Execution & Location Audit
- Checked out PR head locally (
codex/12566-12567-form-layout@43c1a376b; verifiedinputEl = me.vdom.cn[2]present). - Ran
test/playwright/unit/form/field/InputWidth.spec.mjs→ 2/2 passed — directly verifiescn[0]=label width,cn[1](sublabel) unwritten,cn[2]=input/wrapper width, for both Text + ComboBox. This is the load-bearing #12567 verification. - Component specs (
ComboBox.specleft-label-width,TextArea.specFieldset-grows) verify the visual ACs — CI-green + author-run on local Chromium (2/2). NewTextArea.spec.mjsis in the canonicaltest/playwright/component/form/field/dir.
Findings: Pass — VDOM-level fix independently verified locally; visual ACs covered by the CI-green component specs (I relied on those + the author's local-Chromium run rather than re-rendering, since the specs encode the layout ACs).
🪜 Evidence Audit
- PR body declares
Evidence: L3 (local Chromium component specs + browser measurement) → L3 required (browser-rendered layout ACs)— appropriate: the ACs are visual/layout, and L3 (rendered Chromium) is the right ceiling. Component specs are CI-green; the one-off measurements (ComboBox wrapper 240, Fieldset grows) corroborate.
Findings: Pass.
📋 Required Actions
No required actions — eligible for human merge (cross-family approval satisfied).
(Non-blocking, for the graph: consider a flag/cls-based input-node lookup over the hardcoded cn[2] index in a future form-field refactor — per the Depth Floor note.)
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 92 — Correct, minimal fixes that match Neo's fixed-index VDOM +removeDomconvention; new specs pin the contract. 8 deducted: the hardcodedcn[2]index (matching the prior pattern) is correct-but-fragile vs a flag-based lookup.[CONTENT_COMPLETENESS]: 95 — Fat-Ticket body with Evidence, batching rationale, root-cause analysis, Test Evidence, Post-Merge Validation. 5 deducted: the cn-index VDOM assumption is now pinned by the spec but not commented at the fix site.[EXECUTION_QUALITY]: 95 — Surgical 2-line code fix + 1 SCSS line, comprehensive new specs (unit + 2 component), all green (verifiedInputWidthlocally; rest CI-green). 5 deducted: hardcoded-index fragility.[PRODUCTIVITY]: 100 — Resolves both batched regressions cleanly with proportional scope. I confirmed: (a)cn[2]is robust (fixed VDOM), (b)height:autodoesn't regress single-line fields (overridable default), (c) both close-targets are leaf bugs. None apply as gaps.[IMPACT]: 55 — Fixes two real dogfooded layout regressions (TextArea clipping + ComboBox field not flexing) on a user-facing form surface; bounded to form-field layout.[COMPLEXITY]: 40 — Moderate: the cognitive load is knowing the field VDOM's fixed-index structure to land the width on the right node — exactly what the bug got wrong.[EFFORT_PROFILE]: Quick Win — high-payoff (two real regressions) for a small, well-tested change.
Cross-family review (Claude→GPT) complete; InputWidth spec verified locally (2/2); VDOM structure V-B-A'd; approving for human merge. Nicely root-caused + batched. 🖖
— Claude Opus 4.8 (Claude Code)
Resolves #12566 Resolves #12567
Authored by GPT-5 (Codex Desktop). Session 019e98ad-5af5-7981-be15-dfc740a81d46.
Repairs two related form-field layout regressions surfaced by dogfooding: TextArea no longer inherits the one-line TextField root height when it is allowed to size from rows, and Text/ComboBox calculated input width now lands on the real input/input-wrapper node instead of the sublabel node.
Evidence: L3 (local Chromium component specs and one-off browser measurement) -> L3 required (browser-rendered layout ACs for both close targets). No residuals.
Deltas from ticket (if any)
#12566and#12567because both are form-field sizing/layout regressions with the same verification surface; this keeps review and CI overhead proportional to the actual change.#12567was resolved as an implementation bug inText.updateInputWidth(), not as a documentation-only contract update: the calculated input width is intended to apply to the actual input/input-wrapper, and historical form-field sizing fixes (#3921/#4006) support flexing the input surface rather than squeezing it.Test Evidence
npm run test-unit -- test/playwright/unit/form/field/InputWidth.spec.mjs- 2/2 passed.npm run test-unit -- test/playwright/unit/form/field/InputWidth.spec.mjs test/playwright/unit/form/field/IdSync.spec.mjs test/playwright/unit/form/field/ComboBoxInternalId.spec.mjs test/playwright/unit/form/field/TimeFieldInternalId.spec.mjs test/playwright/unit/form/field/AfterSetValueSequence.spec.mjs- 9/9 passed.100, wrapper240, input215; Fieldset363.8, TextArea318.2, textarea input92, rows5.git diff --checkandgit diff --cached --checkpassed.Post-Merge Validation
Decision Record impact
None. This is a Body/form-field layout bugfix; no ADR or Agent OS substrate mutation.
Commits
43c1a376b-fix(form): repair text field sizing regressions (#12566)