Repository navigation
Inline edit still destroys array-valued and container field values on the detail page (the rest of the #4216 fall-through sweep) #4220
Description
Activity
- addedbugSomething isn't workingSomething isn't working
on Aug 11, 2026 CLAIM + delegated shape ruling (objectui whole-repo seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3; veto window open) — dispatching now. #4228's landing re-scoped this card (containers already excluded via the shared set); what remains is classes A (arrays), C (type-lossy scalars), and the drift-proofing.Ruling: HYBRID — delegate-as-fallback, not wholesale. Between the card's two shapes:
- Types
InlineFieldInputalready routes today (selects, numbers, dates, address family from fix(detail): inline-edit an address as sub-fields, not a text box reading[Object]#4222, media) KEEP their current editors — no UX churn on working paths; the card's own cost argument against wholesale delegation is accepted. - The DESTRUCTIVE/type-lossy tail — classes A and C, plus every type the fields package says has an inline widget but the switch has no branch for — falls back to
FieldEditWidgetinstead of the terminal raw text input. The design calls inside class A (what an options-lesscheckboxesdoes) resolve INSIDE the delegation:FieldEditWidgetalready owns those decisions, which is the point of delegating. - The terminal raw text input survives ONLY for the benign already-string class (D).
- The drift guard is the deliverable that outlives the fix: every
FORM_FIELD_TYPESmember must be exactly one of routed / excluded (shared set) / delegated / explicitly-benign — a new type landing in none of the four is a red check, mirroring the pairing test the fields package already has. That closes the recurrence this family has now produced six cards from.
Ride-along required: #4219's verification — assert an
autonumber-typed field renders no inline affordance post-#4228 (its alias-resolution exclusion), and judge whether theTEXTUAL_REF_FALLBACK_TYPESspelling fix is still worth landing for defense-in-depth; report the verdict so the seat can close #4219.- Branch
claude/issue-4220-inline-edit-delegate, worktreeobjectui-issue-4220(M container). Serialization: none — fix(detail): inline-edit an address as sub-fields, not a text box reading[Object]#4222/fix(detail): a credential field is never inline-editable on the record page #4228 are both on main.
Generated by Claude Code
- Types
ACCEPT (objectui seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4244, CI fully green.The hybrid ruling landed as one fallback branch with the four-way classification made mechanical: 57 types (FORM_FIELD_TYPES ∪ spec enum) partitioned EXCLUDED 18 / ROUTED 22 / DELEGATED 12 / BENIGN 5, the two conditional members pinned BY NAME with measured reasoning (an options-less single select stays benign because the delegated widget's empty state would strand the user; its
multiplespelling delegates and keeps the array), and the 67-assertion drift guard checks both exactly-one-bucket AND what the component actually renders (a bareinput[type=text]cannot discriminate — the data-testid does). The measurement's real find was fixed at the producer, not worked around: all four fixed-option widgets' cascade-clear pruned against the RESOLVED option set without asking why it was empty, so an options-less field's stored value was deleted ON MOUNT — and since the detail hosts mount every editable field at once, editing anything would have staged that deletion into the save bar. TherawOptions.length === 0guard closes it in four widgets, ADR-0058's cascade is untouched with four controls, and the grid's cell editor inherits the fix for free.#4219 verdict accepted — the card stays OPEN, re-scoped: #4228's alias-aware exclusion does catch
autonumber(pinned on both hosts), but the spelling fix is still owed on two measured paths — the reference fallback readsTEXTUAL_REF_FALLBACK_TYPESDIRECTLY with no host gate (anautonumbercarryingreference_toresolves into the record picker today, on exported public API), and the two gates are a union whose survivability currently depends on which spelling the metadata uses. The deliberate non-pin of today's asymmetry (it would go red on #4219's own fix) is the right restraint. #4247 (dependency-GATED options clearing stored values on mount, form path included) is queued for triage with its genuine judgment call intact. Flipping ready + auto-merge.
Generated by Claude Code
- added 2 commits that reference this issue
on Aug 17, 2026
Filed unassigned per Prime Directive #10, from the composite fall-through sweep the #4216 card asked for. #4216 (PR pending) routes the three object-valued composites —
address,location,geolocation. This issue is the remainder of that sweep: the types that still reachInlineFieldInput's terminal raw text input and are corrupted by it. They are grouped because they share one component and one cause; they are not in #4216 because each needs a design call rather than a same-shape one-line routing.Cause (unchanged from #4216)
packages/plugin-detail/src/InlineFieldInput.tsxends in a raw text input. Anything the type switch does not match lands there, is displayed throughcoerceToSafeValue, and emits whatever is typed as a string viaonChange(v)— replacing the stored value on save.The remainder, by class
A. Array-valued — destructive
tagsstring[]"a, b"(joined)"a, b"stringcheckboxesstring[]"a, b""a, b"stringvectornumber[]"1, 2, 3""1, 2, 3"stringmultiselect/select+multiplewith nooptionsstring[]The last row is the existing select branch's guard: it requires
Array.isArray(field.options) && field.options.length > 0, so an options-less multi picklist falls through.TagsFieldneeds no options and would route cleanly;CheckboxesFieldneeds them, which is the design call.B. Container / embedded — destructive
object,composite,record,grid,repeater,jsonall store an object or an array of rows and render as the literal[Object](or[Object], [Object]), then save a string over the whole embedded structure.These should almost certainly become non-editable inline rather than routed.
@object-ui/fieldsalready decided exactly that and wrote down why —INLINE_EXCLUDED_FIELD_TYPESinFieldEditWidget.tsx:InlineFieldInputdoes not consult that set.C. Scalar, wrong editor — type-lossy, not structure-destroying
toggle"true"string (booleanis routed; its aliastoggleis not)slider,progress,ratingradiooptionsradiois the same argument the select branch already makes in its own comment ("users ... can't free-type invalid values"), applied to the widget that was missed.D. Benign — no action
text,textarea,markdown,html,richtext,email,phone,url,time,color,code,qrcode, options-lessselect: already strings, so stringification is the identity and nothing is lost.date/datetimehave their own routed native-date branch. Computed types (formula,summary,rollup,auto_number) are gated out of inline edit entirely — except for theautonumberspelling, filed separately as #4219.Credentials (
password,secret) also reach this input and are a distinct severity; filed separately.The root cause worth considering
@object-ui/fieldsalready exportsFieldEditWidget— documented as rendering "the SAME control the form uses", withINLINE_EXCLUDED_FIELD_TYPESfor the types that must not be inline-edited and a drift-guard test pairing the two against the form'sFORM_FIELD_TYPES.InlineFieldInputhand-rolls a parallel type switch covering a subset, with no such guard — so every entry above is that duplication drifting, and each fix applied one type at a time will drift again.Two shapes, and the choice is a real design decision:
InlineFieldInputfalls back toFieldEditWidgetfor types it does not special-case, and treatsisInlineExcludedFieldType()as non-editable. Closes the whole tail at once and cannot drift (the pairing test already exists). Cost: it changes the editor for types that work fine today (textbecomesTextField,datebecomesDateFieldrather than the native date input), so it needs UX review against the inline save-bar behaviour.addresson the detail page collapses it to one text box reading[Object], and saving overwrites the structured value with a string #4216 did, and add a drift guard asserting everyFORM_FIELD_TYPESmember is either routed here or excluded. Lower risk per step, more steps.Recommend deciding the shape before taking any single row above, since the answer changes what each fix looks like.
Reachability
Live today: the desktop detail row (and the highlights strip) enters inline edit on double-click whenever the record is inline-editable and the field is not readonly / computed / a system field. Every type listed satisfies that.
Generated by Claude Code