Repository navigation
fix(plugin-list): filter candidates read the declared displayField, and no list-column options (objectui#10545, objectui#10547) - #10627
Conversation
…nd no id column (objectui#10545) The definition-branch filter candidate in ListView and resolveFields in UserFilters read a lookup's display field as display_field, then reference_field, spellings FieldSchema refuses, and never displayField, the one it declares. Both now read displayField alone, the single spelling plugin-grid's copy set reads (objectui#7155). Neither reads id_field any more: FieldSchema declares no id column for a lookup (it refuses idField and id_field alike), so the value picker keys the lookup by its id default. The objectui#7642 census KEEP comments at both sites are replaced by what the code reads now and why. Claude-Session: https://claude.ai/code/session_01KUxVUa7e39aNjhkKi1gsoy Co-authored-by: Claude <noreply@anthropic.com>
…lter-candidates-displayfield Claude-Session: https://claude.ai/code/session_01KUxVUa7e39aNjhkKi1gsoy Co-authored-by: Claude <noreply@anthropic.com>
…ff a list column (objectui#10547) Before the object definition loads, ListView builds its filter and sort candidates from the declared columns. That fallback read `options` off each column, a key ListColumnSchema refuses with unrecognized_keys. Reusing the objectui#7531 ruling (remove), the read is dropped: a select field's options reach the filter builder from the object definition once it loads. Claude-Session: https://claude.ai/code/session_01KUxVUa7e39aNjhkKi1gsoy Co-authored-by: Claude <noreply@anthropic.com>
…lter-candidates-displayfield Claude-Session: https://claude.ai/code/session_01KUxVUa7e39aNjhkKi1gsoy Co-authored-by: Claude <noreply@anthropic.com>
|
changeset-claim-re-read
|
✅ 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
|
Contract reviewServed-tier: Reviewed from git only: ① Derived judgmentsCard objectui#10545 (chain head), commit
Card objectui#10547 (member), commit
Existing pins edited or deleted: none (file list: 3 added tests, 0 modified). No guard weakened. New pins can fail (each read in full at head):
Clause-②: ② Semver level
Changeset prose, sentence by sentence:
No README or ③ Boundary flags
Implemented-by: VERDICT: PASS |
Fixes #10545
Fixes #10547
Clause-②: no
One claim for two cards on one branch: objectui#10547 is folded into objectui#10545 (its triage: 「Same file as #10545: serial, or one claim」). There is one commit per card and one changeset.
What changed
objectui#10545, commit
0814660:ListView, theobjectDef.fieldsbranch of the filter/sort candidate builder (candidateFields): the candidate readsdisplayField: field.displayFieldand nothing else. It no longer readsdisplay_field,reference_fieldorid_field, and the candidate carries noidField.UserFilters,resolveFields: readsdisplayField = fieldDef.displayFieldand nothing else. TheidFieldthread is gone (the local, the returned member, the internalResolvedFieldmember and the prop handed toLookupValuePicker), because its only writer was theid_fieldread.objectui#10547, commit
61ce977:ListView, the!objectDef?.fieldscolumns-only fallback of the same builder: no longer readsoptions: buildOptions(fieldName, f.options)off a list column. Select options reach the candidate from the object definition once it loads. This reuses the objectui#7531 ruling (5809501335, letter "remove").One changeset,
.changeset/10545-filter-candidates-displayfield.md:patchfor@object-ui/plugin-list, with one paragraph per card. It says plainly that a def carrying only the refuseddisplay_field(or onlyreference_field) no longer sets the picker's display field, thatid_fieldno longer re-keys the picker, and that a list column'soptionsis no longer read.Two merges of
origin/main(1336ba8,f0930bb). The first one (1336ba8) picked up this seat's objectui#10597, which touchesListView.tsxin a different region. Neither merge had conflicts.The contract, re-measured on the installed
@objectstack/spec17.4.0FieldSchema.safeParseof a lookup def (name,type: 'lookup', label,reference) plus one key:zzz_not_a_real_key(control)unrecognized_keys, no hintdisplayFielddisplay_fieldunrecognized_keys: "Did you meandisplay_field→displayField?"reference_fieldunrecognized_keys: "Did you meanreference_field→referenceVia?"id_fieldunrecognized_keys, no hintidFieldunrecognized_keys, no hintListColumnSchema.safeParseof a column (field,label,type: 'select'): the bare column → success; a nonsense key →unrecognized_keys; withoptions→unrecognized_keys: "Unrecognized key(s) on this list column:options." There is no rename hint. The declared keys arefield,label,width,align,hidden,sortable,resizable,wrap,type,pinned,summary,prefix,link,action.The pins restate the refusals as lit-control assertions, so the next spec bump re-derives them.
Why the display read can be
displayFieldaloneplugin-grid.RELATIONAL_META_READ_SETinrelationalMetaKeys.tsclassifiesdisplayFieldasspec, and it is the only display spelling the grid's copy set (RELATIONAL_META_KEYS, viaapplyRelationalMeta) reads off a def (objectui#7155).reference_fieldisno-producerthere and is not copied. So these two sites are no stricter and no looser than the grid's reader.display_fieldstill arrives asdisplayFieldwhen it comes through the in-repo serve paths.normalizeSchemaReferenceKeysfolds it once at ingestion (objectui#7650), inObjectStackAdapter.getObjectSchemaand in theMetadataProviderobject paths. Its alias probe mapsdisplay_fieldtodisplayField.reference_fieldandid_fieldprobe onto no declared key, so the fold leaves them on the document and logs a dev-modeno-declared-twinwarning (objectui#8938). That is the one condition the objectui#7642 PM ruling set for any consumer-side retirement in this family, and it now holds.DataSourcethat bypasses the adapter, or anobjectDefpassed straight to the exportedUserFilters) is held to the sameFieldSchemacontract (AGENTS.md 0.1). The changeset says so.What the sibling
idFieldread becomes: removed, not re-pointedFieldSchemadeclares no id column for a lookup: it refuses bothid_fieldandidField, measured above. So the read targeted no declared key, and re-pointing it toidFieldwould put a new read on an undeclared spelling. The same reasoning is recorded atObjectChartandresolveActionParams.LookupValuePicker(@object-ui/components). It resolves identity withfield.idField || "id", thenr.id ?? r._id. Without the read it always keys byid. On a spec-compliant def the read was always undefined, so nothing changes there.id_fieldon an object def: no non-test hit in objectui. In objectstackorigin/mainthe only hits are the liveness ledger note, tests, and the unreleasedFIELD_KEY_GUIDANCErow inauthoring-key-lint.ts, which says: "id_fieldwas never a FieldSchema key: a lookup stores the referenced record's id … the picker resolves record identity itself."id_field.UserFilterFieldSchema(the view's ownuserFilters.fieldsentry) declares onlyfield,label,type,options,showCount,defaultValues, and it refusesidFieldtoo. So removingResolvedField.idFielddrops no authored value.Pins, and the before/after
packages/plugin-list/src/__tests__/ListView.declaredDisplayField-10545.test.tsx: the candidate FilterBuilder receives once the definition loads (5 tests).packages/plugin-list/src/__tests__/UserFilters.declaredDisplayField-10545.test.tsx: the toolbar lookup picker's resolved label, and the$filterkey of its label lookup (5 tests).packages/plugin-list/src/__tests__/ListView.columnsOnlyOptions-10547.test.tsx: the columns-only candidate before load, plus the lit control where options arrive with the definition (3 tests).Each ablation leg was run under the shared verify lock. It checks out the BASE version of each source file with the pins unchanged, proves on disk that the swap landed (
grep -cof the pre-fix and post-fix lines), and restores withgit checkout HEAD --. The restore is proved by an emptygit diff HEADand by blob hashes equal to HEAD's, inside anEXIT INT TERMtrap.0814660, sources atf169a03:PINS_HEAD_EXIT=0, 10 passed (10).PINS_BASE_EXIT=1, 8 failed and 2 passed (10). The 2 that pass are the spec lit-control test and the picker-default control.display_fieldandreference_fieldnegative legs);id_field);id_fieldre-keyed the lookup, so the label never resolved).f0930bb,ListView.tsxat1336ba8:PIN10547_HEAD_EXIT=0, 3 passed (3).PIN10547_PREFIX_EXIT=1, 1 failed and 2 passed. The failing test is "a column carryingoptionsoffers the same candidates as a plain one": "expected [ …(2) ] to be undefined". The spec test and the lit control stay green in both legs.Gates, at
f0930bb(the last commit)pnpm exec vitest run --maxWorkers=2 packages/plugin-list/(lock): exit 0,Test Files 97 passed (97),Tests 1144 passed (1144).pnpm turbo run type-check --filter @object-ui/plugin-list --concurrency=2(lock): exit 0.tsc --noEmit && tsc -p tsconfig.test.json, thenTasks: 14 successful, 14 total.tsc -p tsconfig.test.json --listFileslists all 3 new pin files.check:control-bytes,check:test-path-roots,check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape,check:new-line-citations,check:handler-key-reads,check:changeset-claims,check:pending-changeset-literals: all exit 0. The line-citation gate printed "VERDICT new-cross-file-line-citations: 0 new citation(s)"..github/workflows/:check:unreferenced-sources,check:phantom-deps,check:self-import,check:designer-field-key-parity, and thecheck-changeset-presence/-no-major/-fixed/-overwritescripts. All exit 0. Presence printed "5 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)".files: ['**/*.{ts,tsx}']covers all 5. ②--format jsonreports 5 files. ③ The config enables no type-aware linting (projectService/parserOptions.projectcount 0), so this diff cannot move any untouched file's verdict.ListView.tsx, 30 onUserFilters.tsx) equal the same files at BASE. The new test files have 0.pnpm lint, fullpnpm testand the other workflows are CI's. There was no browser run. The pins render the realListView,UserFiltersandLookupValuePickerunder happy-dom.Acceptance notes
plugin-formderiveMasterDetail(deriveColumnsandhydrateColumns) still readsd?.display_field || d?.reference_fieldand never the declareddisplayField. This is the same seam, at a site this card does not claim. It is a source reading on this tree, not probed.Seam: spec:FieldSchema.displayField → renderer:plugin-form deriveColumns / hydrateColumns (GridColumn.displayField).@object-ui/fieldsLookupFieldandLookupCellRendererstill readreference_fieldas a second display leg (gradedno-producerin the grid's register). Not touched; no carrier.ObjectChartandresolveActionParamskeep thedisplay_field/reference_fieldlegs behinddisplayField(objectui#7435). The tree now holds two dispositions for the same refused spellings; this card follows the grid's. No carrier..changeset/lucky-donkeys-shave.md(objectui#7642) describes both plugin-list sites as KEEP. This PR supersedes that for these two sites. The file declares no package, so it publishes into no CHANGELOG. It is not edited here.candidateFieldsmemo, the older objectui#6837 note besidereferenceTostill says the columns-only branch "is NOT narrowed". objectui#7531 and objectui#10547 have since narrowed it. Not edited (outside the claimed lines); no carrier.Generated by Claude Code