Repository navigation
Field creation: a searchable type picker; Add field asks for label and type before creating #12516
Description
Source
Measured on objectui main d4bc81eab, 2026-10-15. "Add field" (the Form tab toolbar and the object header's ⋯ menu) creates a field at once named "New field" with api name field_10 and type Text, then opens the field inspector; the designer renames and retypes it afterwards. The inspector's Type control (metadata-admin/inspectors/FieldTypePicker.tsx) is a listbox of 45 types in 8 groups (Text 10, Number 3, Date / time 3, Logic 2, Selection 4, Relation 4, Media 5, Calculated 3, Advanced 15), each with a one-line description, and no search box; reaching "Picklist" means scrolling past the Text, Number, Date and Logic groups.
Target
- The type listbox opens with a search input focused at the top; typing filters on the type's label, api value and description (
pickleaves Picklist alone,lookleaves Lookup); groups stay as headings over the matches. - "Add field" opens a small popover with Label (required; api name derived as the inspector does today) and Type (the same searchable picker, Text preselected) and a Create button; the field is created with those values and opens in the inspector as today.
- The empty-form "Add field" and the Form toolbar's share the popover.
File surface
packages/app-shell/src/views/metadata-admin/inspectors/FieldTypePicker.tsx, packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (the add-field handler), ObjectFormDesigner.tsx, packages/app-shell/src/views/metadata-admin/i18n.ts, tests beside.
Acceptance
- Tests: typing
pickshows Picklist only; the popover creates a field named as typed with the chosen type. - Screenshots of the picker with a query and of the popover, light and dark.
pnpm --filter @object-ui/app-shell testgreen.
Constraints
- The 45 types and their groups do not change.
- Existing
data-testids stay.
Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff from the 2026-10-15 Studio test pass: objectui main d4bc81eab, HotCRM 63db83e2, the vite console on :5180 against objectstack serve --dev on :4001, viewport 1482 × 1119, dark theme with a light-theme check.
Evidence
Screenshots from the test pass, kept on the temporary branch claude/studio-test-pass-evidence-20261015 of objectstack-ai/objectui (commit 72115588c, never merged: the maintainer ruled that screenshots do not enter main, and asked for a temporary branch referenced from the card). The branch's docs/design/studio-redesign/findings/2026-10-15/README.md maps every file to its card. Pane captures, 800 × 604 of a 1482 × 1119 viewport, dark unless named light.

docs/design/studio-redesign/findings/2026-10-15/12516-add-field-creates-new-field.jpg — add field creates new field.

docs/design/studio-redesign/findings/2026-10-15/12516-type-picker-no-search.jpg — type picker no search.
Parent: #12446
Body refreshed 2026-10-15T15:19Z by the epic PM session: an Evidence section with the test-pass screenshots added.
Activity
objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 1 (domain:ui lane queue; an objectui#12446 test-pass card)
Session:
session_01SJn1GEkrbbBQkhGC26wEDwAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12516-field-type-pickerWorktree:
objectui-issue-12516(in the cloud session's own container)Domain:
domain:uiSeat:
domain:ui#4File surface:
packages/app-shell/src/views/metadata-admin/inspectors/FieldTypePicker.tsx(the search input and filter),packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx(theDataPillaradd-field handler and its callers only),studio-design/ObjectFormDesigner.tsx(the toolbar and empty-form "Add field" entries), one new popover component beside them if the shared entry needs it,metadata-admin/i18n.ts(new keys only), new tests beside them, one.changeset/12516-*.md(stop on breach; explain in the report)Container & model:
M,mode:cloud,model: opus(cloud on the maintainer's stance 「可以的话,ui 后续开发尽量用云 session,并且要求截图给我看」; lane scope on 「你也可以接ui车道的卡」; batch 5 on 「上限提到 5,提速」, this session's chat, 2026-10-15)Clause-②: no
Responsibility:
n/a — not a defect cardThread-read: none
Serial constraints cleared: objectui#12515 (the Form tab preview,
ObjectFormDesigner.tsx's toolbar) landed as PR objectui#12576 (e089a25), and objectui#12511 retired the object header's More menu (5249c24), so "Add field" now lives on the Form toolbar, the empty form and the Records grid's "+" column. In flight onStudioDesignSurface.tsx, this seat: objectui#12510 (accessible names; it may give the Records grid's "+" add-field control anaria-label), objectui#12514 (the Automations and Access empty branches) and objectui#12522 (theInterfacesPillarpage branch). Any textual overlap is resolved by mergingorigin/main; a name objectui#12510 gives that control stays.objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{
"issue": 12516,
"status": "done",
"branch": "claude/issue-12516-field-type-picker",
"pr": null,
"session": "session_01NexEdg9sJzmsdpTuKVo4AY",
"premise_still_valid": true,
"summary": "Target 1 premise was partly false (mechanism assumption 1 falsified): FieldTypePicker already had a cmdk search input (objectui#11793), focused on open via Radix auto-focus, filtering name/category/id/English name with Enter picking; the card's 'no search box' screenshot is a pane crop with the input above the visible area of a top-flipped popover. What #12516 adds: description matching on whole words (Han/Kana as substring), because a substring match would make
pickalso find Slider ('A number picked on a slider.') and break the card's own acceptance. Targets 2-3: a new AddFieldPopover (required Label, the same FieldTypePicker with Text preselected, Cancel/Create) now fronts every Add field entry. On main those entries are the Form toolbar and the Records grid '+' column; there is no empty-form Add field (assumption 2 partly falsified). Both entries run the same DataPillar addField (same buffer, same autosave), so both get the popover. The api name is toFieldNameLoose(label), the inspector's derivation; it falls back to the next field_N when the result is empty or collides, as the inspector keeps the default in both cases. Read-only packages still offer no Add field. Card assignee untouched; claim comment 6203079516 names this branch.","tests": "At 04e029f (final head, after merging origin/main f96f771):
pnpm --filter @object-ui/app-shell testrun in full once, under the verify lock (nohup plus a foreground tail --pid wait): EXIT=0, 'Test Files 1416 passed | 1 skipped (1417)', 'Tests 13521 passed | 9 skipped (13530)', lock VERDICT command-exit 0. Targeted, before the merge, on every suite reaching add-field or the picker (18 files incl. the 2 new): 'Test Files 18 passed (18) / Tests 90 passed (90)'; the new popover suite was re-run after its last edit at b659c47: 8 passed. New pins: FieldTypePicker.search-12516.test.tsx (8 tests: focus on open,pickgives Picklist only under Selection,lookgives Lookup only under Relation, whole-word description match,pickedfinds Slider butpickdoes not, groups and empty-group drop, ArrowDown+Enter, zh Han substring) and DataPillar.addFieldPopover-12516.test.tsx (8 tests: focused required Label, Type on Text, Create disabled until a non-blank label; Create giveshealth_scoreof the chosen type and opens the inspector; the picker'spicksearch inside the popover; Cancel and Escape create nothing and save nothing; collision and non-Latin label give field_2/field_3; grid '+' opens the same popover and focus returns to '+'; leaving the grid does not reopen it; read-only control). Ablation, one mutation each, anchor proven on disk (count 1, then 0 after the mutation), restored withgit checkout HEAD -- path,git diff HEADempty after each: A1 remove the description match gave 4 red; A2 description as substring gave 2 red (incl. 'typing "pick" shows Picklist only'); A3 name not derived from the label gave 2 red; A4 collision unhandled gave 1 red; A6 popover clicks recorded as the grid button gave 1 red (focus return). A5 (unmount close) stayed green, which showed the code unreachable (an outside click closes the popover first), so it was deleted in b659c47 rather than kept. No dist resolution is involved: app-shell tests import src.","gates": "Local, at 04e029f, exit codes captured before any pipe:
pnpm --filter '@object-ui/app-shell^...' build0 (re-run after the merge);pnpm --filter @object-ui/app-shell type-check0 (echoed 'tsc --noEmit && tsc -p tsconfig.test.json');pnpm --filter @object-ui/app-shell build0 ('dist completeness: 1 package(s) complete');pnpm --filter @object-ui/app-shell test0 (counts above); eslint on the 13 touched app-shell ts/tsx files 0 errors (warnings are all on pre-existing lines; the one my first draft added, set-state-in-effect, was removed);pnpm check:i18n-keys0;pnpm check:i18n-drift0 ('No en value changed');pnpm check:i18n-designer-parity0 ('Every en row has a zh row');pnpm check:i18n-dead-keys0 (no new candidates: newFieldLabel is now the Label placeholder);pnpm check:new-line-citations0 ('0 new citation(s)');node scripts/check-changeset-presence.mjs0 ('14 source file(s) of 1 released package(s) changed ... declares 1 changeset(s)'). objectui has no dispatch-gates.mjs, so the gate list was derived by hand from the dispatch. CI families: no PR yet, so no check-runs to cite; NOT MEASURED: CI, reason: the PM opens the PR.","mcp_calls": "2: mcp__github__issue_read (get, get_comments) on objectui#12516, both reads; no MCP write tool. Also 2 add_repo calls (claude-code-remote, read access, nothing attached) and 2 unauthenticated REST GETs of epic comments 6188626991 and 6192878850.",
"api_writes": "0 REST writes. git push only: claude/issue-12516-field-type-picker (empty-branch probe, then 6 pushes ending at 04e029f) and claude/issue-12516-evidence (15adf9a). No label writes, no PR, no card comment.",
"open_questions": [
{
"question": "Card target 1 asks for a description match while its acceptance needs
pickto show Picklist only. A substring match on descriptions breaks that (Slider: 'A number picked on a slider.'). Which reading stands?","options": [
"A: description matched on whole words, Han/Kana as substring (implemented)",
"B: no description match (the pre-card behaviour; the target 1 clause goes unmet)",
"C: substring match plus rewording the Slider description (changes an existing key's text in all locales; outside the 'new keys only' surface)"
],
"recommendation": "A, on the four axes. Real need: the card's own queries (
pick,look) pass, and words that appear only in descriptions ('masked' finds Password and Secret) start to work. Long term: no copy is edited to suit a matcher. AI-error axis: the rule is pinned both ways (pickedfinds Slider,pickdoes not). Startup axis: about 20 lines, no new keys."},
{
"question": "The Records grid '+' gets the same popover (assumption 2: it runs the same addField). The grid draws that button from GridFieldAuthoring, whose
onAddColumn: () => voidhands back no element, and @object-ui/components exports no PopoverAnchor. So the popover sits at the records body's top-right corner, not under '+' (focus still returns to '+'). Should a follow-up change the components contract to pass the anchor?","options": [
"A: accept the corner placement (implemented)",
"B: follow-up card:
onAddColumn(anchor: HTMLElement)in gridFieldAuthoring.tsx and data-table.tsx (components package, outside this claim's surface)","C: leave the grid '+' creating instantly (the three entries disagree)"
],
"recommendation": "A now, B only if the maintainer finds the corner placement wrong in practice. B is a two-line contract change, but there is no pull for it yet (startup axis). C brings back the 'New field / field_N' behaviour the card exists to remove."
},
{
"question": "After a popover create, the field's api name (e.g. health_score) is not a placeholder shape, so the inspector's Label no longer moves it (objectui#7615's ownership rule treats it as locked). Previously a fresh field_N followed later label edits. Acceptable?",
"options": [
"A: accept; the author can still edit the API name box (implemented)",
"B: have the inspector treat a just-created name as label-owned (ObjectFieldInspector.tsx, outside the surface)"
],
"recommendation": "A. The label is asked for up front now, so later label-driven renames are the exception. Changing the inspector's ownership state machine is a separate decision with its own pins."
}
],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted, not filed: GridFieldAuthoring.onAddColumn is typed
() => voidwhile data-table passes the click event, so a host cannot place UI against the '+' button without reading an undeclared argument. Not a/b/c class (no metadata, no runtime refusal); recorded in the PR's Acceptance notes.","carrier: none · noted, not filed: the card body says '45 types in 8 groups' but lists 9 categories (Text 10, Number 3, Date/time 3, Logic 2, Selection 4, Relation 4, Media 5, Calculated 3, Advanced 15, summing to 49); the catalog was not touched, per the constraint."
],
"deviations": [
"Contract vs dispatch: os-dev.md says to open the draft PR and post the os-dev-report card comment through the scripts/pm relay. The dispatch's degraded channel forbids both because the fleet-write relay is refused (403) from this container. The file wins on paper but can't be carried out here, so the dispatch's route was followed: pr null, body below, PM posts.",
"File surface: besides the claimed files, 7 existing test files (DataPillar.formToolbar-12451, DataPillar.layoutBadge, DataPillar.savedAtDisplayLocale-10232, StudioDesignSurface.autosaveInFlight-11204, StudioDesignSurface.autosaveSwitch-11232, StudioDesignSurface.draftVersionConflict-11773) and the shared helper studio-design/tests/dataPillarAddField.ts were updated. Their Add field click now opens the popover (the helper fills it with 'New field'), and expected names moved from field_2/field_3 to new_field, with field_4 where a second 'New field' collides. No assertion was weakened.",
"Target 3: there is no empty-form Add field on main (an empty section shows 'Drop here'). The entries that share the popover are the Form toolbar and the Records grid '+'.",
"The grid '+' popover was checked in tests only. In the browser, the draft-only object shows no grid (no published table), so there is no screenshot of that entry.",
"Commit trailers use AGENTS.md's model-free pair (Co-authored-by: Claude, plus Claude-Session) instead of the harness's model-named Co-Authored-By, per AGENTS.md precedence."
],
"files_changed": [
"packages/app-shell/src/views/metadata-admin/inspectors/FieldTypePicker.tsx",
"packages/app-shell/src/views/studio-design/AddFieldPopover.tsx (new)",
"packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (DataPillar addField, addedFieldName, grid popover)",
"packages/app-shell/src/views/studio-design/ObjectFormDesigner.tsx (toolbar entry)",
"packages/app-shell/src/views/metadata-admin/i18n.ts (1 new key, en + zh: engine.studio.data.addFieldCreate)",
"packages/app-shell/src/views/metadata-admin/inspectors/FieldTypePicker.search-12516.test.tsx (new)",
"packages/app-shell/src/views/studio-design/DataPillar.addFieldPopover-12516.test.tsx (new)",
"packages/app-shell/src/views/studio-design/tests/dataPillarAddField.ts and 6 existing suites (see deviations)",
".changeset/12516-add-field-popover.md"
],
"screenshots": [
"https://github.com/objectstack-ai/objectui/blob/15adf9afff4b4199949266ff64e0eedf78676c60/docs/design/studio-redesign/findings/12516/12516-type-picker-pick-light.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/15adf9afff4b4199949266ff64e0eedf78676c60/docs/design/studio-redesign/findings/12516/12516-type-picker-pick-dark.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/15adf9afff4b4199949266ff64e0eedf78676c60/docs/design/studio-redesign/findings/12516/12516-add-field-popover-light.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/15adf9afff4b4199949266ff64e0eedf78676c60/docs/design/studio-redesign/findings/12516/12516-add-field-popover-dark.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/15adf9afff4b4199949266ff64e0eedf78676c60/docs/design/studio-redesign/findings/12516/12516-created-light.png?raw=true",
"https://github.com/objectstack-ai/objectui/blob/15adf9afff4b4199949266ff64e0eedf78676c60/docs/design/studio-redesign/findings/12516/12516-created-dark.png?raw=true"
],
"line_budget": "n/a",
"pr_title_draft": "feat(app-shell): Add field asks for label and type in a popover; the type picker also searches descriptions (objectui#12516)",
"pr_body_draft": "(elided by the seat: opened verbatim as the body of PR #12587, read back identical)"
}
Relayed verbatim by seat
domain:ui#4(sessionsession_01SJn1GEkrbbBQkhGC26wEDw) from the cloud session's final message, the degraded write channel. The seat opened the draft PR #12587 frompr_title_draftandpr_body_draft, with assigneeos-sales."pr": nullabove is the session's own value at report time.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Landed · PR #12587 merged as
1b3b5b1onmain· read 2026-10-16T00:22Zorigin/main1b3b5b1function addedFieldNameinStudioDesignSurface.tsxstudio-design/AddFieldPopover.tsxengine.studio.data.addFieldCreateinmetadata-admin/i18n.tsFieldTypePicker.search-12516.test.tsx,DataPillar.addFieldPopover-12516.test.tsx,.changeset/12516-add-field-popover.mdState change: the card closed as completed through
Fixes #12516, andpm:dispatchedis removed in this act. The cloud sessionsession_01NexEdg9sJzmsdpTuKVo4AYis archived.Generated by Claude Code