You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Lookup picker: the subtitle repeats the record name when the referenced object's name field is not name — every AI-built lookup #25860
Filing gate ① a reproduced UX defect (class a, public door: a lookup picker on a record form). reach: local stack, objectui pin 6f629a7 (cloud main36b07fdf), zh-CN, 2026-10-25. In an AI-built app, the 关联客户 picker on 新建家装项目 lists each customer as the title 「周明轩」 over a subtitle that repeats it: 「客户姓名: 周… · 联系电话: 1862457… · 客户来源: 门店」.
Batch-of: 6 · 2026-10-25 desktop E2E, second pass (magic-flow UX, reviewed in-session). Filed at the maintainer's request: 「必要的合理的建议都可以立卡」 / 「体验问题 也请fable 审核是否需要立卡」.
Business reason. The subtitle exists to tell similar records apart. Its first, and widest, slot is spent on the name the title already shows, which pushes the phone number (the field that actually disambiguates two 王静 records) into truncation. This happens on every lookup to an object whose name field is not literally name, which is every AI-built app.
Reader who acts. The repo:objectui ui seat: packages/fields/src/widgets/LookupField.tsx. objectui-only: AI-built lookups declare no displayField / descriptionField, so no metadata change is needed.
The option title resolves through getRecordDisplayName → objectDef.nameField (core/src/utils/record-title.ts:26-31), here customer_name.
previewColumns (:585-599) seeds seen with displayField ('name') only. deriveLookupColumns.ts:86 leads with that absent name, so customer_name becomes the first subtitle item.
objectui#10343 (closed) fixed the title ladder, not the subtitle's seen set.
Expected
seen also holds the field the title actually resolved from (objectDef.nameField, or whatever getRecordDisplayName used), so the subtitle starts with 联系电话.
Dedupe
gh api search/issues on objectui, incl. closed:
lookup descriptionField repeats name: 0.
lookup picker subtitle display field nameField: 1, a seat card.
DEFAULT_DISPLAY_FIELD nameField lookup: 1. objectui#10343 (closed) is the other half (the title).
Transferred 2026-10-26T01:52Z from objectstack-ai/objectui#13542, with its comments and timeline; the old URL redirects here. objectui moved into this repository as the ui/ workspace (epic #25677) and is being archived (#25854). The body is left exactly as filed.
Paths. Read every repository path in the body with a ui/ prefix: packages/fields/src/widgets/LookupField.tsx is now ui/packages/fields/src/widgets/LookupField.tsx.
⛔ Not a claim, ⛔ not a dispatch. First touch after the transfer from objectui. The card had no triage audit comment there or here, which is what the domain:ui seat (#25966) needs before it can pick it.
p3, as filed: the picker's subtitle spends its first slot on the name the title already shows. It is a UX cost and nothing is wrong in the data. The fix: skip the object's display (name) field in the subtitle, whatever that field is called. Paths are under ui/.
Claim: PM loop round 1 · 2026-10-26T08:46Z
Session: session_01SMps7wq7rBTk53e8KTSEup
Account: os-steve
Branch: claude/issue-25860-lookup-subtitle-name-field
Worktree: objectstack-issue-25860
Domain: domain:ui
Seat: domain:ui#2
File surface: ui/packages/fields/src/widgets/LookupField.tsx (the previewColumns memo and its imports only), a new or existing LookupField test beside it; scripts/sdui-manifest.record.json (re-record); root .changeset/25860-lookup-subtitle-name-field.md (stop on breach; explain in the report). ⛔ ui/.github/** and the root .github/workflows/ci.yml are out of bounds (maintainer's word). ⛔ ui/packages/fields/src/index.tsx is held by #25897 / #25937.
Container & model: S, mode:subagent, model: default tier (dispatch-gates --tier --repo objectstack-ai/objectstack: no path-derived mandate), effort: medium
Clause-②: no
Metadata-protocol: none
Responsibility: the lookup's own preview list, which skips only the configured display field | the record-title resolver already knows the object's real name field | every lookup to an object whose name field is not literally name, in the picker subtitle
Thread-read: 6410157597
Premise: holds 2026-10-26T08:42Z — on origin/main6a6ebff97fLookupField.tsxpreviewColumns seeds seen with displayField only, and @object-ui/core exports resolveNameField (utils/record-title.ts:366)
Serial constraints cleared: none — no open claim holds LookupField.tsx.
Writes: PR only via the relay op pr_create; comments only via the relay write tools; ⛔ no GitHub MCP writes.
Ruling section: triage 6410157597.
Suggested route: also skip the referenced object's resolved name field when building previewColumns (seen.add(resolveNameField(refObjectSchema)), guarded for undefined); test a lookup whose target's name field is title and confirm the subtitle no longer repeats it.
{
"issue": 25860,
"status": "done",
"branch": "claude/issue-25860-lookup-subtitle-name-field",
"pr": "https://github.com/objectstack-ai/objectstack/pull/26062",
"session": "session_01SMps7wq7rBTk53e8KTSEup",
"premise_still_valid": true,
"summary": "Measured premise on origin/main 6a6ebff97f: previewColumns in ui/packages/fields/src/widgets/LookupField.tsx seeded seen with displayField only (the 'name' guess when undeclared), while recordToOption titles the option through getRecordDisplayName (declared nameField, else titleFormat, else derived title field); resolveNameField is exported from @object-ui/core. Fix: when the lookup declares no displayField, previewColumns also adds resolveNameField(refObjectSchema) to seen, except when a titleFormat (and no declared name field) titles the record; deps gain declaredDisplayField and refObjectSchema, imports gain declaredNameField and resolveNameField. A declared displayField and an authored descriptionField behave as before. scripts/sdui-manifest.record.json re-recorded on the merged tree (registry digest only; sdui.manifest.json byte-unchanged); patch changeset .changeset/25860-lookup-subtitle-name-field.md for @object-ui/fields. Card assignee untouched; PR assignee os-steve.",
"tests": "New ui/packages/fields/src/widgets/LookupField.subtitleNameField-25860.test.tsx (5 cases: declared nameField 'title' -> subtitle [phone, source]; derived 'title' -> same; authored descriptionField leads [source, phone]; controls: declared displayField keeps 'title' in subtitle; titleFormat-only keeps derived 'title'). All under os-verify-lock: (1) closure build turbo run build --filter='@object-ui/fields^...' VERDICT command-exit 0 (46/46 tasks); (2) cd ui && pnpm exec vitest run --maxWorkers=2 packages/fields/ at a200f71d: Test Files 278 passed | 1 skipped (279), Tests 4295 passed | 7 skipped (4302), VERDICT command-exit 0 (merge brought no change under ui/packages/fields, core or types); (3) at dcee0130 after merging origin/main: new test + labelOrder-10343 + optionDescription + previewBadges-13123: Test Files 4 passed (4), Tests 21 passed (21), VERDICT command-exit 0; (4) pnpm --filter @object-ui/fields type-check (tsc --noEmit && tsc -p tsconfig.test.json, which includes src/**/*.test.tsx) TC_EXIT=0. Ablation (fix committed first, test imports source, no dist): node scripts/ablation-replace.mjs replaced 'if (nameField) seen.add(nameField);' (anchor 1 -> 0, blob 04c7cbb215e3 -> b6be553ded33) -> Tests 3 failed | 2 passed (5), the 3 behaviour cases red, both controls green; restored blob == HEAD 04c7cbb215e3 and git diff HEAD empty.",
"gates": "Local at dcee0130: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 46 commands; all 46 exit 0 (exit captured per command before any pipe), incl. node scripts/check-sdui-manifest.mjs (present, intact, registry sha256 d71fa5f15902), pnpm check:nul-bytes, pnpm check:type-check-coverage, check-empty-changeset, check-changeset-no-major, check-adr-0087-registration. --ran reconciliation with exit codes: 46 derived, 46 run, 0 NOT-MEASURED (a DERIVED zero). CI on PR #26062 at dcee0130: in_progress at report time (Type Check, Lint lanes, Check Changeset, Governed Surface Queue Guard, card-claim checks); 'No other open PR may claim the same issue' success. Not waited on per contract.",
"dogfood_battery": "n/a — no in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2 — (1) fleet-write relay POST /repos/objectstack-ai/objectstack/dispatches executing pr_create (POST /pulls + POST /issues/26062/assignees), read back identical 5454 bytes, assignees os-steve; (2) this os-dev-report comment via scripts/pm/post-stamped.mjs (POST /issues/25860/comments). git push not counted.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none — noted in PR Acceptance notes, not filed: effectiveDescriptionField still falls back to the first non-displayField picker column, which for an undeclared lookup can be the name field; it only feeds the option's description (popover text match), not the rendered subtitle; outside this card's file surface; no user-visible effect measured."
]
}
Activity
objectstack-fleet commentedon Oct 26, 2026
Transferred 2026-10-26T01:52Z from objectstack-ai/objectui#13542, with its comments and timeline; the old URL redirects here. objectui moved into this repository as the
ui/workspace (epic #25677) and is being archived (#25854). The body is left exactly as filed.Paths. Read every repository path in the body with a
ui/prefix:packages/fields/src/widgets/LookupField.tsxis nowui/packages/fields/src/widgets/LookupField.tsx.No stale
pm:dispatchedor assignee to strip.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Triage: first touch —
bug·priority:p3·domain:ui·area:records·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-26T06:58Z⛔ Not a claim, ⛔ not a dispatch. First touch after the transfer from objectui. The card had no triage audit comment there or here, which is what the
domain:uiseat (#25966) needs before it can pick it.ui/.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 1 · 2026-10-26T08:46Z
Session:
session_01SMps7wq7rBTk53e8KTSEupAccount:
os-steveBranch:
claude/issue-25860-lookup-subtitle-name-fieldWorktree:
objectstack-issue-25860Domain:
domain:uiSeat:
domain:ui#2File surface:
ui/packages/fields/src/widgets/LookupField.tsx(thepreviewColumnsmemo and its imports only), a new or existing LookupField test beside it;scripts/sdui-manifest.record.json(re-record); root.changeset/25860-lookup-subtitle-name-field.md(stop on breach; explain in the report). ⛔ui/.github/**and the root.github/workflows/ci.ymlare out of bounds (maintainer's word). ⛔ui/packages/fields/src/index.tsxis held by #25897 / #25937.Container & model:
S,mode:subagent,model: default tier (dispatch-gates --tier --repo objectstack-ai/objectstack: no path-derived mandate),effort: mediumClause-②: no
Metadata-protocol: none
Responsibility:
the lookup's own preview list, which skips only the configured display field|the record-title resolver already knows the object's real name field|every lookup to an object whose name field is not literally name, in the picker subtitleThread-read: 6410157597
Premise: holds 2026-10-26T08:42Z — on
origin/main6a6ebff97fLookupField.tsxpreviewColumnsseedsseenwithdisplayFieldonly, and@object-ui/coreexportsresolveNameField(utils/record-title.ts:366)Serial constraints cleared: none — no open claim holds
LookupField.tsx.Writes: PR only via the relay op
pr_create; comments only via the relay write tools; ⛔ no GitHub MCP writes.previewColumns(seen.add(resolveNameField(refObjectSchema)), guarded for undefined); test a lookup whose target's name field istitleand confirm the subtitle no longer repeats it.objectstack-fleet commentedon Oct 26, 2026
os-dev-report
{ "issue": 25860, "status": "done", "branch": "claude/issue-25860-lookup-subtitle-name-field", "pr": "https://github.com/objectstack-ai/objectstack/pull/26062", "session": "session_01SMps7wq7rBTk53e8KTSEup", "premise_still_valid": true, "summary": "Measured premise on origin/main 6a6ebff97f: previewColumns in ui/packages/fields/src/widgets/LookupField.tsx seeded seen with displayField only (the 'name' guess when undeclared), while recordToOption titles the option through getRecordDisplayName (declared nameField, else titleFormat, else derived title field); resolveNameField is exported from @object-ui/core. Fix: when the lookup declares no displayField, previewColumns also adds resolveNameField(refObjectSchema) to seen, except when a titleFormat (and no declared name field) titles the record; deps gain declaredDisplayField and refObjectSchema, imports gain declaredNameField and resolveNameField. A declared displayField and an authored descriptionField behave as before. scripts/sdui-manifest.record.json re-recorded on the merged tree (registry digest only; sdui.manifest.json byte-unchanged); patch changeset .changeset/25860-lookup-subtitle-name-field.md for @object-ui/fields. Card assignee untouched; PR assignee os-steve.", "tests": "New ui/packages/fields/src/widgets/LookupField.subtitleNameField-25860.test.tsx (5 cases: declared nameField 'title' -> subtitle [phone, source]; derived 'title' -> same; authored descriptionField leads [source, phone]; controls: declared displayField keeps 'title' in subtitle; titleFormat-only keeps derived 'title'). All under os-verify-lock: (1) closure build turbo run build --filter='@object-ui/fields^...' VERDICT command-exit 0 (46/46 tasks); (2) cd ui && pnpm exec vitest run --maxWorkers=2 packages/fields/ at a200f71d: Test Files 278 passed | 1 skipped (279), Tests 4295 passed | 7 skipped (4302), VERDICT command-exit 0 (merge brought no change under ui/packages/fields, core or types); (3) at dcee0130 after merging origin/main: new test + labelOrder-10343 + optionDescription + previewBadges-13123: Test Files 4 passed (4), Tests 21 passed (21), VERDICT command-exit 0; (4) pnpm --filter @object-ui/fields type-check (tsc --noEmit && tsc -p tsconfig.test.json, which includes src/**/*.test.tsx) TC_EXIT=0. Ablation (fix committed first, test imports source, no dist): node scripts/ablation-replace.mjs replaced 'if (nameField) seen.add(nameField);' (anchor 1 -> 0, blob 04c7cbb215e3 -> b6be553ded33) -> Tests 3 failed | 2 passed (5), the 3 behaviour cases red, both controls green; restored blob == HEAD 04c7cbb215e3 and git diff HEAD empty.", "gates": "Local at dcee0130: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 46 commands; all 46 exit 0 (exit captured per command before any pipe), incl. node scripts/check-sdui-manifest.mjs (present, intact, registry sha256 d71fa5f15902), pnpm check:nul-bytes, pnpm check:type-check-coverage, check-empty-changeset, check-changeset-no-major, check-adr-0087-registration. --ran reconciliation with exit codes: 46 derived, 46 run, 0 NOT-MEASURED (a DERIVED zero). CI on PR #26062 at dcee0130: in_progress at report time (Type Check, Lint lanes, Check Changeset, Governed Surface Queue Guard, card-claim checks); 'No other open PR may claim the same issue' success. Not waited on per contract.", "dogfood_battery": "n/a — no in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "2 — (1) fleet-write relay POST /repos/objectstack-ai/objectstack/dispatches executing pr_create (POST /pulls + POST /issues/26062/assignees), read back identical 5454 bytes, assignees os-steve; (2) this os-dev-report comment via scripts/pm/post-stamped.mjs (POST /issues/25860/comments). git push not counted.", "open_questions": [], "out_of_scope_findings": [ "carrier: none — noted in PR Acceptance notes, not filed: effectiveDescriptionField still falls back to the first non-displayField picker column, which for an undeclared lookup can be the name field; it only feeds the option's description (popover text match), not the rendered subtitle; outside this card's file surface; no user-visible effect measured." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
ACCEPT → PR #26062 comment 6414061234 · seat 2 · 2026-10-26T10:52Z
objectstack-fleet commentedon Oct 26, 2026
Landed · seat 2 · 2026-10-26T15:25Z
dfaad4da, headdadc26bd(fields re-record after #26120)origin/main7111e14a, containsdfaad4da(git merge-base --is-ancestorexit 0)gh-readonly-queue/main/pr-26062-*: none left (consumed)Fixes #25860on #26062Status:
pm:dispatchedremoved; the card is done. Nothing remains.