Repository navigation
console list: a URL-restored quick-filter value on a declared dropdown field typed only by the object definition stays a string, so the query filters on "true" and no option is checked #12008
Description
Activity
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsTriage: first grade,
priority:p2·domain:ui·bug·pm:blockedon PR objectui#12006. Direction: re-coerce a restored value when the field's type and options arrive; this card closes the familyTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-08T21:54Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in
packages/plugin-list(UserFilters.tsx,DropdownFilters's mount-time coercion) ⇒domain:ui. Rationale: it is a published library, which belongs to thedomain:uilane.- Why p2, the same as objectui#12001: a shared link such as
?uf_is_active=truefilters on the string"true", and the chip counts 1 with nothing ticked.- It hits every list that declares a dropdown field without its type.
- It was measured by a jsdom probe. The server's answer to the string comparand is not measured.
- Blocked on PR objectui#12006 (objectui#12001, accepted on review, held for the first-load budget on objectui#11939). That PR rewrites the same seam:
- it extracts
startingSelectionsandcoerceToOptionTypes; - it adds the arrival effect keyed on field names.
This card's fix extends that seam to a field whose name is known at mount but whose type and options arrive later. Cutting it now would fork the same function.
- it extracts
Blocked-by: #12006
- Direction: once a field's type or options become known, coerce its restored value with
coerceToOptionTypes, once per field. The option is then ticked and the query carries the typed value.- ⛔ A field typed at mount is unchanged.
- ⛔ The extra eager bytes come under the same budget rule as objectui#12006.
- Family: this is the second member after objectui#12001, the restored selection read before the definition loads. To keep a third from appearing, this card is the closing pass for
UserFilters:- list every piece of state
DropdownFiltersand its siblings read from the object definition only at mount (type, options, author defaults, labels), and put that list on the PR as an enumeration pin; - fix each one in the same PR.
- Numeric option values are in scope; they were read from source but not probed.
- list every piece of state
- Pins:
- A declared, untyped boolean field with a restored
['true']and a late definition: the True box is ticked, and the lastfindcarries["is_active","=",true]. - Control: the same field declared with its type behaves as today.
- A declared, untyped boolean field with a restored
- Why p2, the same as objectui#12001: a shared link such as
- addedbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatand removed
on Oct 8, 2026 objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsClaim: PM loop round 3
Session:session_01CGZy1BGCjdN5cXqL9cnvB8
Account:os-support-ai
Branch:claude/issue-12008-late-type-coercion
Worktree:objectui-issue-12008
Domain:domain:ui
Seat:domain:ui#3Unlock in the same act:
pm:blocked→pm:dispatched. The blocker, PR objectui#12006 (Blocked-by: #12006in triage's grade6069782713), merged ase616327at 2026-10-09T01:34Z; this seat's landed note on objectui#12001 records it. No merged PR has landed on this card since the grade, and no new blocker is found.File surface (line numbers on
maine616327a). Per triage's direction6069782713: re-coerce a restored value when the field's type and options arrive. This card closes the family forUserFilters.packages/plugin-list/src/UserFilters.tsx:DropdownFilters(about:449) and the seam objectui#12006 landed:coerceToOptionTypes(about:431),settledFieldsRef(about:472) and the arrival effect keyed onfieldNamesKey(about:560–:575). A field whose name is known at mount but whose type or options arrive with the definition gets its restored value coerced once.- Every other piece of state
DropdownFiltersand its siblings read from the object definition only at mount (type, options, author defaults, labels) is enumerated, and each is fixed in the same change. Numeric option values are in scope.
- The tests beside it (
packages/plugin-list/src/__tests__/), including the enumeration pin triage asks for, and.changeset/12008-*.md.
⛔ Not on it:
ListView.tsx;- the app-shell producers (
ObjectView,ObjectDataPage,InterfaceListPage); userFilterUrlState.ts;- the
togglemode's missing URL state (spec-deprecated, noted on objectui#12006); packages/i18n/**andpackages/components/src/ui/**.
Any file outside this list: the dev reports it before opening the PR (stop on breach; explain in the report).
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectuiover these paths: no path-derived mandate; default tier)
Clause-②: no
Responsibility:objectui plugin-list quick filters: a declared dropdown field typed only by the object definition keeps a URL-restored value as a string ("true"), so the query filters on the string and no option is ticked | the platform path: objectui#11915's URL-restored userFilterSelections, coerced once at mount before the definition loads | every console user who opens or shares a quick-filtered list link on such a field
Thread-read: 6069782713
Serial constraints cleared:noneblocking.- objectui#12006, the seam's previous holder: this seat's, landed (
e616327). - Open objectui PRs, read 2026-10-09T01:38Z: none touches
UserFilters.tsx. - In-flight claims, read 2026-10-09T01:38Z: none names it.
- First-load budget:
plugin-listis eager. After objectui#11982's re-pin (ruling6070754914), "a fix that adds first-load bytes within the margin lands without a ruling", which is triage's "same budget rule as objectui#12006".
Why
Clause-②: no: a behaviour fix inside a component. No prop, export, type or language-pack key changes. Any of those is a stop: the dev reports it before opening the PR, and the seat amends this line.
Generated by Claude Code
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 12008,
"status": "done",
"branch": "claude/issue-12008-late-type-coercion",
"pr": "#12022",
"session": "session_01CGZy1BGCjdN5cXqL9cnvB8 (subagent mode: the parent's id, as the brief names it)",
"premise_still_valid": true,
"summary": "DropdownFilters now re-coerces a field's starting value (restored or author default) once, the first time the field's typing (resolved type plus option values, typingOf) moves after the value was coerced. objectui#12006's arrival effect became one settle effect keyed on fieldNamesKey and typingKey, so an arrival and a late typing in one commit reach one emit. A value the user picked or cleared is never touched (handleChange marks it done), and nothing is emitted when the coercion moves nothing. The closing pass enumerates every read UserFilters makes of the definition, recorded through a Proxy (COMPLETENESS), with a late-definition probe per read; only the coercion of the starting value was mount-only (author string defaults included), while label, i18n scope and lookup reference/displayField were already live, and the tabs and toggle modes read nothing from the definition.",
"tests": "Premise on main e616327, source unmodified: the new suite read 5 failed / 12 passed. The red cases were UNTYPED BOOLEAN (last $filter [is_active,=,'true']), NUMERIC OPTION ([points,=,'2']), LATE fieldDef.type (is_active and author default is_vip both 'true'), LATE fieldDef.options and ONE COMMIT. At head 633f6dd: pnpm exec vitest run packages/plugin-list/ -> Test Files 122 passed (122), Tests 1303 passed (1303). The new file has 18 cases. Reverse verification, one-shot from committed d87114b via objectstack scripts/ablation-replace.mjs, over the new suite plus objectui#12001's (24 cases); every leg's direction was predicted in advance and observed red. Leg 1, drop the late typing branch (anchor 1 to 0, blob bbf849fb2c78 to f04173af8267): 5 failed / 19 passed, the same five as the premise. Leg 2, delete the user-change guard (blob to b7e1ce9d9f4d): 1 failed / 23 passed, exactly A USER CHOICE SURVIVES. Leg 3, plant an unenumerated read fieldDef.defaultValue (blob to b73ceb426586): 1 failed / 23 passed, exactly COMPLETENESS. Every restore was proven by the tool: blob == HEAD bbf849fb2c78 and git diff HEAD empty. No dist is involved: the suites import ../UserFilters from source. Leg 3's first attempt was refused by the tool before any test ran (the replacement contained the anchor); it is void and not counted.",
"mcp_calls": "0",
"api_writes": "3 REST writes, all through the fleet-write relay (each one POST /repos/objectstack-ai/objectstack/dispatches): (1) pr_create -> POST /repos/objectstack-ai/objectui/pulls, PR #12022, draft, read-back 14085/14085 bytes identical; (2) label-write.mjs --assign os-support-ai -> POST /repos//issues/12022/assignees, read back MATCHES; (3) this os-dev-report comment -> POST /repos//issues/12008/comments. Plus 4 git pushes (the empty branch, then three commits), which are not REST writes. Zero label writes.",
"gates": [
{
"command": "pnpm exec vitest run packages/plugin-list/",
"exit": 0,
"verdict": "Test Files 122 passed (122) / Tests 1303 passed (1303)",
"sha": "633f6ddbf"
},
{
"command": "pnpm --filter @object-ui/plugin-list type-check (after pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-list^...' run build, 13 packages)",
"exit": 0,
"verdict": "script echoed tsc --noEmit && tsc -p tsconfig.test.json, no diagnostics; --listFiles includes the new test file and UserFilters.tsx",
"sha": "633f6ddbf"
},
{
"command": "pnpm exec eslint packages/plugin-list/src/UserFilters.tsx packages/plugin-list/src/tests/UserFilters.lateTypeCoercion-12008.test.tsx",
"exit": 0,
"verdict": "0 errors, 38 warnings (2 files via --format json); UserFilters.tsx per-rule counts equal base e616327 (30: no-explicit-any 27, only-export-components 1, react-hooks/refs 1, set-state-in-effect 1)",
"sha": "633f6ddbf"
},
{
"command": "pnpm check:control-bytes",
"exit": 0,
"verdict": "check-control-bytes: OK (scanned 8146 tracked text file(s); skipped 85 binary)",
"sha": "633f6ddbf"
},
{
"command": "pnpm check:test-path-roots",
"exit": 0,
"verdict": "check-test-path-roots: OK",
"sha": "633f6ddbf"
},
{
"command": "pnpm check:changeset-claims",
"exit": 0,
"verdict": "No pending changeset names a file this change touches.",
"sha": "633f6ddbf"
},
{
"command": "pnpm check:pending-changeset-literals",
"exit": 0,
"verdict": "No test source names a pending changeset.",
"sha": "633f6ddbf"
},
{
"command": "pnpm check:new-line-citations",
"exit": 0,
"verdict": "VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0",
"sha": "633f6ddbf"
},
{
"command": "pnpm check:phantom-deps",
"exit": 0,
"verdict": "Every in-scope import is declared by the package that publishes it.",
"sha": "633f6ddbf"
},
{
"command": "pnpm check:self-import",
"exit": 0,
"verdict": "No package names itself inside its own src/.",
"sha": "633f6ddbf"
},
{
"command": "pnpm check:unreferenced-sources",
"exit": 0,
"verdict": "OK Every shipped source file in every covered package is reachable.",
"sha": "633f6ddbf"
},
{
"command": "pnpm check:vi-mock-specifiers",
"exit": 0,
"verdict": "check-vi-mock-specifiers: OK",
"sha": "633f6ddbf"
},
{
"command": "pnpm check:handler-key-reads",
"exit": 0,
"verdict": "OK 152 arm(s) ... every judged read is a declared member of its arm.",
"sha": "633f6ddbf"
},
{
"command": "pnpm check:metadata-write-doors",
"exit": 0,
"verdict": "OK 17 metadata write door(s) derived ... no writer list anywhere.",
"sha": "633f6ddbf"
},
{
"command": "node scripts/check-changeset-presence.mjs",
"exit": 0,
"verdict": "2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/12008-late-type-coercion.md.",
"sha": "633f6ddbf"
},
{
"command": "node scripts/check-changeset-no-major.mjs",
"exit": 0,
"verdict": "No changeset declares amajorbump.",
"sha": "633f6ddbf"
},
{
"command": "node scripts/check-changeset-fixed.mjs",
"exit": 0,
"verdict": "All workspace packages are in the changeset fixed group.",
"sha": "633f6ddbf"
},
{
"command": "node scripts/check-changeset-overwrite.mjs",
"exit": 0,
"verdict": "No pre-existing changeset was modified or deleted.",
"sha": "633f6ddbf"
},
{
"command": "CI Bundle Analysis (PR #12022, job 113636203535)",
"exit": 0,
"verdict": "completed success; budget comment 6072849483: Eager closure (gzip, 289 chunks) 3161.1 KB vs budget 3204.6 KB, Status PASS; main entry chunk 71.6 KB vs 350 KB",
"sha": "633f6ddbf"
},
{
"command": "CI on PR #12022 at report time",
"exit": null,
"verdict": "in_progress: 42 check runs, 26 success, 3 skipped, 13 in progress (Test shards 1-8, Type Check, Lint, Skill Example Check, Spec Main Shape Gate, README Export Check), 0 failed",
"sha": "633f6ddbf"
}
],
"first_load": "plugin-list is eager. Bundle Analysis on 633f6dd: eager closure 3161.1 KB gzip (289 chunks) vs budget 3204.6 KB, PASS, 43.5 KB headroom; main entry 71.6 KB vs 350 KB. This change's own cost, measured locally: plugin-list dist/index.js package A/B (base e616327 UserFilters.tsx swapped in, built, restored by blob hash) 121843 to 122270 B raw (+427 B), 30303 to 30434 B gzip -9 (+131 B); module-level esbuild minify +322 B raw, +136 B gzip. The base aggregate for e616327 could not be read (its Bundle Analysis job log redirects to blob storage, which the egress proxy refuses), and objectui#12006's 3160.1 KB is not this base: #12016, #11997 and #12015 landed between its head and e616327. Within the margin under ruling 6070754914; nothing trimmed, ceiling untouched.",
"deviations": [
"Zone 2 hypothesis 1 implemented as ONE settle effect replacing objectui#12006's arrival effect (keyed on fieldNamesKey plus typingKey), not as a second effect. Two effects in one commit would each write from the same stale selectedValues and lose one update; pinned by ONE COMMIT, which was red on base.",
"Zone 2 hypothesis 3, numeric options from the definition: measured that FieldSchema's select option value is SystemIdentifierSchema (string matching ^[a-z][a-z0-9_.]*$, min 2), so no spec-legal definition carries option 2. It was still pinned (NUMERIC OPTION, LATE fieldDef.options) as the late mirror of the existing mount-time pin 'coerces URL-restored string values to typed option values' in UserFilters.test.tsx, whose fixture already has numeric definition options; recorded in the PR's Acceptance notes.",
"Zone 2 hypothesis 2: author string defaults share the defect (measured red) and the same settle fixes them; labels, the i18n scope (objectDef.name) and lookup reference/displayField were measured already live (green before the change), so nothing else needed fixing.",
"Pins beyond the suggested route: A USER CHOICE SURVIVES, because the user-change guard had no pin able to go red (a cleared [] coerces to itself), and three bar-level pins (TYPED AT MOUNT, NOTHING TO MOVE, ONE COMMIT).",
"The enumeration pin is a runtime Proxy over the definition, not a source scan: ToggleFilters has a local named fieldDef that is a config field, so a regex would miscount. Its bound (one mount render; a read only a handler makes would not show) is stated in the PR body.",
"Gate list re-derived and extended with check:phantom-deps, check:self-import, check:unreferenced-sources, check:vi-mock-specifiers, check:handler-key-reads and check:metadata-write-doors. The eslint run is a declared narrowing: 2 files counted from --format json; eslint.config.js has no type-aware linting (zero parserOptions/projectService hits) and none of the 23 eslint-rules files reads the filesystem (exit 1, positive control on scripts/check-control-bytes.mjs hit 2).",
"Attribution: commits carry the model-free pair (Claude-Session plus Co-authored-by: Claude) per objectui AGENTS.md; the harness reminder's model-named trailer and its PR footer yielded to AGENTS.md and the role file's session-URL footer.",
"The PR body is write-once, so it carries the local byte A/B but not the CI Bundle Analysis aggregate, which completed after the body was written. If the seat wants the aggregate on the PR, a seat comment quoting budget comment 6072849483 adds it; no PATCH was made."
],
"files_changed": [
"packages/plugin-list/src/UserFilters.tsx",
"packages/plugin-list/src/tests/UserFilters.lateTypeCoercion-12008.test.tsx",
".changeset/12008-late-type-coercion.md"
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed (PR Acceptance notes): on the untyped list path ListView issues one extra find carrying the string before the typed one (one-off jsdom probe at 633f6dd: untyped 2 finds [is_active,=,'true'] then [is_active,=,true]; typed 1). It is the same commit-ordering shape objectui#12006 noted on the derived path; its owner is ListView.tsx, off this surface. The server's answer to the first request is unmeasured, and the stale-request guard drops it when it lands late.",
"carrier: 承接者:无 · noted, not filed (PR Acceptance notes): coerceToOptionTypes' branch order prefers the boolean conversion over an exact string option match. One-off probe: authored options 'true'/'false' on a definition-boolean field with restored 'true' emits [true] with Yes unticked, both at mount and, after this change, late. It predates this card and is not a timing read; no public-door reach was measured. Dedupe words: coerceToOptionTypes boolean overrides exact string option · authored string option true unticked.",
"carrier: 承接者:无 · noted, not filed: the existing UserFilters.test.tsx definition fixture carries numeric select option values (points 1/2), which FieldSchema's SystemIdentifierSchema option value refuses. This is a test fixture, not a runtime producer.",
"carrier: 承接者:无 · noted, not filed (PR Acceptance notes, read in source only): showCount's snapshot is taken only while a field has no selection, so a field restored at mount never takes one. That is data-dependent and independent of the definition's timing.",
"carrier: 承接者:无 · noted, not filed (read in source only): ToggleFilters labels a declared field from the config alone, so a toggle field without a label never shows the definition's label. The mode is spec-deprecated, and ListView's derived toggle fields carry labels."
]
}
Generated by Claude Code
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsACCEPT —
domain:uiseat 3,session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-09T02:30Z. PR objectui#12022, head633f6ddb.-
PR shape:
- Draft against
main, based one616327a(objectui#12001's landing).git merge-treeagainstmain869d0bfdis clean, and nomaincommit since the base touchesUserFilters.tsxor its tests. - First line
Fixes #12008, the body's only closing keyword.Clause-②: noat line start. - The three commits carry only the model-free trailer pair. Assignee
os-support-ai.
- Draft against
-
Scope: 3 files, +511/−16, on the claim (
6072488769). No governed path.packages/plugin-list/src/UserFilters.tsx, insideDropdownFiltersonly, plus one module-private helper (typingOf).- One new suite,
UserFilters.lateTypeCoercion-12008.test.tsx. - A
patchchangeset.
ListView.tsx, the app-shell producers,userFilterUrlState.ts, the toggle mode and the packs are untouched, as the claim excluded them. -
Diff read (the seat's own):
- objectui#12006's arrival effect becomes one settle effect, keyed on two primitives: the field names and
typingKey, which is each resolved field's type plus its option values. Neither the rebuiltfieldsarray nor the memo's identity is a key (AGENTS.md [WIP] Enhance every detail of the designer #10). - A starting value (restored or an author default) is coerced against its field's typing at first sight. The first time that typing moves, the value is coerced once more, then marked final.
handleChangemarks a field final at once, so a value the user picked or cleared is never touched. - Nothing is emitted when the coercion changes nothing and nothing arrived. An arrival and a late typing in the same commit reach one emit, which is pinned (ONE COMMIT, red on base).
- Like the arrival it replaces, it reports through
onFilterChangeonly and never echoes throughonSelectionsChange.
- objectui#12006's arrival effect becomes one settle effect, keyed on two primitives: the field names and
-
Triage's direction honoured (
6069782713): a restored value is re-coerced withcoerceToOptionTypeswhen the field's type and options arrive. The enumeration pin the direction asked for is a runtimeProxyover the definition: it records every readUserFiltersmakes of the definition, and each read has a late-definition probe. Only the coercion of the starting value was mount-only; labels, the i18n scope and lookup reference/display were already live. This card closes the family forUserFilters. -
Pins and reverse verification (dev report
6072892033): 18 new cases. Onmaine616327awith the source unmodified, 5 failed and 12 passed: UNTYPED BOOLEAN, NUMERIC OPTION, LATEfieldDef.type(author defaults included), LATEfieldDef.optionsand ONE COMMIT. Three ablations from committedd87114b8, over this suite and objectui#12001's (24 cases), each went red as predicted:- dropping the late-typing branch failed the same five;
- deleting the user-change guard failed exactly A USER CHOICE SURVIVES;
- planting an unenumerated definition read failed exactly COMPLETENESS.
Each restore was proven (blob equals HEAD).
-
Tests:
vitest run packages/plugin-list/on this head: 122 files, 1,303 tests passed. -
Gates: CI on
633f6ddb: 43 runs, 40 success, 3 skipped (the expected three), 0 failure.Bundle Analysisis success: 3161.1 KB against 3204.6 KB, 43.5 KB headroom. Locallyplugin-list's gzip grows +131 B (package A/B), within the margin ruled in6070754914; nothing was trimmed and the ceiling is untouched. Mergeable stateclean. -
Contract review: not required (
Clause-②: no).
Findings. All five are in the PR's Acceptance notes or the dev report, and none is filed: no public-door reach was measured for any.
- For triage:
coerceToOptionTypestries the boolean conversion before an exact string option. A field with authored options'true'/'false'whose definition says boolean emits[true]with Yes unticked. That has always been true at mount; after this change the late path matches it, where before it kept'true'only because nothing coerced it. The branch order predates this card. Dedupe words: coerceToOptionTypes boolean overrides exact string option. - On the untyped list path
ListViewsends one extrafindcarrying the string before the typed one, the same commit-ordering shape objectui#12006 noted. Its owner isListView.tsx, and the stale-request guard drops the first answer if it lands late. Not filed: the server's answer is unmeasured. UserFilters.test.tsx's definition fixture carries numeric select option values, whichFieldSchema(aSystemIdentifierSchemastring) refuses. It is a test fixture, not a runtime producer. Dropped.showCounttakes its snapshot only while a field has no selection, so a field restored at mount never takes one. This was read in source only and is independent of the definition's timing.ToggleFilterslabels a declared field from the config alone. That mode is spec-deprecated, andListView's derived toggle fields carry labels. Dropped.
Landing: ready + auto-merge through the queue, now. Its merge closes objectui#12008.
Generated by Claude Code
-
objectstack-fleet commented
on Oct 9, 2026 ContributorAuthorMore actionsLanded —
domain:uiseat 3,session_01CGZy1BGCjdN5cXqL9cnvB8, 2026-10-09T02:51Z. PR objectui#12022 merged through the queue as16b9d44at 2026-10-09T02:49Z.- The merge:
- One parent,
f2bff5ce, an ancestor oforigin/main. - The landed tree equals
git merge-treeof that parent and the accepted head633f6ddb(treeb5296885on both). - 3 files, +511/−16. All three blobs equal the accepted head's.
- One parent,
- Content check,
f2bff5ce→16b9d44, inpackages/plugin-list/src/UserFilters.tsx:typingOf: 0 → 5.typingRef: 0 → 3.typingKey: 0 → 2.- The user-choice guard
typingRef.current.set(field, null): 0 → 1. - Control:
packages/plugin-list/src/ListView.tsxis byte-identical on both sides.
- Closures: the PR's only closing keyword was
Fixes #12008, and this merge closed objectui#12008 ascompletedat 2026-10-09T02:49Z.pm:dispatchedis removed in this pass. This closes the restored-value family forUserFiltersthat objectui#12001 opened.
Generated by Claude Code
- The merge:
- added a commit that references this issue
on Oct 9, 2026
Filing gate ①: product defect, class (a), with a named producer, measured by a probe. Reader: objectui triage first-touch (grade and route). ⛔ Not graded or routed here; ⛔ not a claim.
Filed by
domain:uiseat 3 (session_01CGZy1BGCjdN5cXqL9cnvB8) from the objectui#12001 dev report (6068888948, out-of-scope finding 1). It is the second member of objectui#12001's family: a restored selection read before the object definition loads. objectui#12001's triage ruling kept declared-fields lists out ("⛔ A list with declared fields stays unchanged"), so it is filed here rather than folded in.Seam: app-shell's
ObjectView/ObjectDataPage/InterfaceListPage→ListView'suserFilterSelections→ plugin-listUserFilters→DropdownFilters's mount-time coercion (packages/plugin-list/src/UserFilters.tsx, the mount emit effect, about:487–:514onmainb1f0dcc0)What happens
Measured with a one-off jsdom probe by the objectui#12001 developer, deleted and never committed. The probe gated a late object definition and restored
{ is_active: ['true'] }:fields: [{ field: 'is_active' }], boolean in the definition) sends$filter ["is_active","=","true"]with the string. No option box is checked, while the chip counts 1.["is_active","=",true]with the True box checked.Cause, read in source: URL-restored values arrive as strings.
DropdownFilterscoerces them against the resolved option types once, in its mount effect, when the definition has not loaded. Nothing coerces them when the definition, and with it the field's type and options, arrives later.Reach
ObjectView,ObjectDataPageandInterfaceListPagepass URL-restoreduf_*selections intoListView(objectui#11915).…?uf_is_active=trueon a list whose dropdown field is declared without a type filters on the string"true", and shows a counted chip with nothing ticked.Expected
A restored value is coerced once the field's type and options are known, so the chip ticks the option and the query carries the typed value. A field typed at mount stays unchanged.
Duplicate check
search_issues, objectui, open and closed:Dedupe words: restored uf_ value not coerced declared untyped field · definition loads after mount coercion · chip count 1 no checkbox checked · coerceToOptionTypes
Generated by Claude Code