Uh oh!
There was an error while loading. Please reload this page.
fix(fields): stop classifying strings by shape in coerceToSafeValue - #7343
Merged
Conversation
A `code` value whose TEXT is JSON rendered as the literal `[Object]`. The
showcase Field Zoo record's Code Editor field (`f_code`) stores the string
`{\n "ok": true\n}` and the detail page showed `[Object]`.
`coerceToSafeValue` parsed any string starting `{`/`[` and ending `}`/`]`,
then ran the result through the reference-label extraction
(`name || label || externalId || id || _id || '[Object]'`). An object carrying
none of those keys answers the placeholder. Every text-like cell reaches this
helper — `text`, `textarea`, `code`, `time`, `auto_number` and `qrcode` all
register to `TextCellRenderer` — so all of them lost JSON-shaped text, and
`[1, 2, 3]` in a text field rendered as `1, 2, 3`.
Shape is not a type. A string is now returned verbatim. The reference case the
parse was written for (#1426, an unresolved external-id ref arriving as
`'{"externalId":"…"}'`) belongs to reference-TYPED columns and already lives
there: `LookupCellRenderer` carries its own JSON-string branch that resolves the
label through the referenced object's schema and links to the record — neither
of which the type-blind helper could do. Scoped, not dropped.
Object and array VALUES still coerce, so React error #310 stays fixed.Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
hotlong
marked this pull request as ready for review
September 2, 2026 16:16
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#7246
What was wrong
The showcase Field Zoo record
Specimen — Fullstores its Code Editor field (f_code, typecode) as the string{\n "ok": true\n}. The detail page rendered the literal[Object].coerceToSafeValueclassified strings by shape: any string starting{/[and ending}/]wasJSON.parsed, and the result fell through to the reference-label extraction (name || label || externalId || id || _id || '[Object]'). An object carrying none of those keys answers the placeholder.Every text-like cell reaches that helper —
text,textarea,code,time,auto_numberandqrcodeall register toTextCellRenderer— so all of them lost JSON-shaped text, and[1, 2, 3]in a text field rendered as1, 2, 3.The fix
A string is returned verbatim, whatever its shape. Object and array values still coerce, so React error #310 stays fixed.
Scoped, not dropped. The reference case the parse was written for (objectui#1426 — an unresolved external-id ref arriving as
'{"externalId":"…"}') belongs to reference-typed columns, and it already lives there:LookupCellRenderercarries its own JSON-string branch that resolves the label through the referenced object's schema and links to the record — neither of which the type-blind helper could do. Per AGENTS.md #0.1, shape-sniffing in a generic text path is exactly the second de-facto contract that commandment bans.Call-site census (
coerceToSafeValue, every surface declared)packages/fields— Text/Number/Currency/Percent/Date/DateTime/Email/Url/Phone/Formula cell renderers (10 sites)packages/fields—LookupCellRenderer(lookup/master_detail/tree)coerceToSafeValue; owns its own JSON-string branch. This is where the behaviour is scoped to.packages/plugin-detailInlineFieldInput.tsx:506typeof value === 'object'— the string branch was unreachable from it.packages/plugin-gridObjectGrid.tsx(5 sites, mobile card + record detail)getCellRenderer(fieldDef.type)first, so lookups reachLookupCellRenderer. Full package suite green.packages/componentsdata-table.tsxsafeObjectLabelString(value)).Verification
Union run at
3a80fcf79(final commit).Reverse verification — the deleted branch restored from
origin/main, mutation confirmed on disk by blob hash (a22a8afb…!= HEADa6bc6c9e…) and by marker count (2 occurrences vs 1):Restore leg proven byte-exact:
git diff HEADempty, blob back toa6bc6c9e….Tests (
pnpm exec vitest runfrom repo root, per AGENTS.md — notpnpm --filter, which is the silently-green trap):Type-check:
packages/fieldspnpm run type-checkexit 0 (tsc --noEmit && tsc -p tsconfig.test.json). Both edited test files confirmed present in the test program via--listFiles(1 hit each) — the "typecheck excludes tests" blind spot does not apply here.Changeset gates:
check-changeset-presence.mjsandcheck-changeset-no-major.mjsboth exit 0.Lint — declared narrowing. Ran on the 3 changed files rather than the whole repo, with the three pieces of evidence:
isPathIgnored()isfalsefor all three changed files.--format json: 3 files linted, 0 errors, 143 warnings (139 pre-existingno-explicit-anyinindex.tsx; 4 in the new test fromfield={{ type } as any}, the convention every existing cell-renderer test uses). Exit 0.parserOptions.projectandprojectServicebothnull), so this diff cannot move the verdict on any untouched file.Shared verify lock — declared narrowing. Every heavy command was routed through
scripts/pm/os-verify-lock.sh, which reported verbatim:(macOS ships no
flock; the lock is declared Linux-only.)Browser verification
Own isolated stack (backend
objectstack dev --fresh -p 4011, consolevite --port 5193 --strictPort; both stopped by recorded PID afterwards — the PM's:3911service was already gone, nothing of anyone else's was touched). Console aliases@object-ui/fieldstopackages/fields/src, so the page ran the patched source.The premise reproduced independently from the API:
f_code typeof = string, value"{\n \"ok\": true\n}". Same live page, DOM read directly:[Object]on pageCode Editor[Object]trueCode Editor{\n "ok": true\n}falseReference columns on that same record still resolve to labels after the fix —
Northwind(lookup → account) andWebsite Relaunch(master-detail → project) — so the scoped behaviour is confirmed live, not only in unit tests.Tests added
packages/fields/src/__tests__/textCellJsonText-7246.test.tsx— the reporting value verbatim; object- and array-literal strings acrosstext/textarea/code; object/array values still coercing; and a CONTROL case pinning that a reference column still unwraps'{"externalId":"Website Relaunch"}'.packages/fields/src/coerce-safe-value.test.ts— two cases replaced, not respelled: they pinned exactly the deleted branch, so their assertions are now the wrong answer at this seam. Their scenario moved to the control case above. A third was corrected from a JSON-string array to a real array, which is the shape that actually reaches a cell.Generated by Claude Code