Repository navigation
console: 47 native <select> elements beside the shared Select/Combobox — the Group panel, the validation editor and Explain access among them #11865
Description
Activity
- addedenhancementNew feature or requestNew feature or requestdomain: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 seatarea:studioChanging a running app without code — authoring, publish, docs and the portalChanging a running app without code — authoring, publish, docs and the portal
on Oct 8, 2026 objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsPath: write metadata — one control for one kind of choice | 缺项 | P3
Triage: first grade,
enhancement·priority:p3·domain:ui·area:studio·pm:queue. Filed as ②, routed: the shared Select/Combobox already exist; replacing native selects with them changes no contractTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-08T04:05Z. ⛔ Not a claim, ⛔ not a dispatch.Triage: lands in the 47 native
<select>elements in 30 source files (the Group panel,ObjectValidationsPanelandAccessExplainPanelfirst) ⇒domain:ui; rationale: a Studio or console surface in objectui. Read on objectuimain4e7dd56ed1.- Why not the decision box: the repository already has its select primitive; this is consistency work. Why p3: every select works.
- Direction:
- replace native selects with the shared Select (or Combobox where the list is long), surface by surface
- start with the three the maintainer named
- the read-only state follows the primitive (studio(data): Field properties inspector stays open on Validations/Hooks/Actions/API/Settings, and read-only packages keep edit wording ("Draft layout", "saved as draft") #11781)
- Land it in several PRs under this card; each names the files it converts.
Clause-②: no. Patch changeset in objectui.
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsClaim: PM loop round 3
Session:session_01MgfduSkFrfM3eorB3UGfAU
Account:os-bill
Branch:claude/issue-11865-grouping-editor-select
Worktree:objectui-issue-11865
Domain:domain:ui
Seat:domain:ui#2
Scope of this claim: the list Group panel only, the first of the three surfaces triage names. The PR saysPart of #11865, and the card stays open for the rest.ObjectValidationsPanelis held by this seat's objectui#11861, in flight.AccessExplainPanelis held by objectui#11862.- Each is claimed after its holder lands.
File surface (line numbers on
e81c846):packages/components/src/custom/grouping-editor.tsx: its native<select>(about:84) becomes the sharedSelect(orComboboxabove the threshold triage names), with the read-only state following the primitive (objectui#11781).GroupingEditorPropsis unchanged.- The tests beside it, and
.changeset/11865-*.md.
⛔ Not on it:
- Any change to
GroupingEditorPropsor to an export ofpackages/components/src/custom/index.ts. packages/components/src/ui/**: the Shadcn-synced primitives are used, not edited.- A lint rule against raw
<select>(card option A). A new gate defaults to no and is not in triage's direction. - The other 46 selects.
packages/i18n/**.
Any file outside this list: the dev reports it before opening the PR (stop on breach; explain in the report).
Container & model:S,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectuiover these paths: no path-derived mandate; default tier)
Clause-②: no
Responsibility:objectui components grouping-editor: the list Group panel picks its field with a native <select> beside the Filter panel's comboboxes, and in a read-only context it looks editable | the shared Select/Combobox primitives already exist and carry the read-only state (objectui#11781) | every console user grouping a list, seen in the filer's 2026-10-07 browser QA pass
Thread-read: 6051973479
Serial constraints cleared:noneblocking.- In-flight claims read 2026-10-08T08:02Z: none names
grouping-editor.tsx. - Open objectui PRs, read 2026-10-08T08:02Z: none touches it.
- objectui#11860: this seat's former claim on the list's URL state (grouping) is back with triage, and it holds no file.
Why
Clause-②: no: the component's internal control changes; its props and exports do not. Nopackages/i18nkey is added.
Generated by Claude Code
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 11865,
"status": "done",
"branch": "claude/issue-11865-grouping-editor-select",
"pr": "#11934",
"session": "session_01MgfduSkFrfM3eorB3UGfAU",
"premise_still_valid": true,
"summary": "GroupingEditor (the list Group panel, both mount points: ListView's toolbar Group popover and ViewSettingsPopover's Group section) now picks each level's field with the shared Radix Select instead of a native select element; GroupingEditorProps, the package exports and packages/components/src/ui/** are unchanged. Select, not Combobox, because the Filter and Sort field pickers beside it are both Select, and the Filter list (the object's fields) is a superset of the Group panel's column list (H1 measured; the card's 'comboboxes' are Radix Select triggers). The value written is byte-identical to the native control's (read on the base component); one display change: a level grouped by a field outside fieldOptions now shows that field's name, where the native control showed the first option. H3 and H4 are falsified for this surface: no disabled/readOnly channel exists under frozen props, and neither the old nor the new control ever had an accessible name. PR is draft, Part of #11865 (the card stays open for the validation editor and Explain access).",
"files_changed": [
"packages/components/src/custom/grouping-editor.tsx",
"packages/components/src/tests/grouping-editor-shared-select-11865.test.tsx",
".changeset/11865-grouping-editor-shared-select.md"
],
"gates": {
"head": "b5272f93d (git rev-parse --short HEAD after the last commit; every gate below ran on it; remote branch head b5272f9)",
"vitest_consumers": "repo-root pnpm exec vitest run --maxWorkers=2 on the new suite + packages/plugin-list/src/tests/ListView.test.tsx + packages/plugin-list/src/tests/ViewSettingsPopover.rowColorTitle.test.tsx + packages/core/src/utils/tests/column-identity.ratchet.test.ts: 'Test Files 4 passed (4)', 'Tests 170 passed (170)', lock VERDICT command-exit 0",
"vitest_components_suite": "repo-root pnpm exec vitest run --maxWorkers=2 --shard=1/2 packages/components/: 'Test Files 185 passed (185)', 'Tests 1813 passed | 17 skipped (1830)', VERDICT command-exit 0; --shard=2/2: 'Test Files 184 passed | 1 skipped (185)', 'Tests 1970 passed | 7 skipped (1977)', VERDICT command-exit 0",
"type_check": "pnpm --filter @object-ui/components type-check (script echoed: tsc --noEmit && tsc -p tsconfig.test.json) after pnpm --workspace-concurrency=2 --filter '@object-ui/components^...' run build (VERDICT command-exit 0): VERDICT command-exit 0; tsc --listFilesOnly shows grouping-editor.tsx in the main program and both touched files in the test program",
"eslint": "pnpm exec eslint on the two touched .ts(x) files: exit 0; --format json: files 2, errors 0, warnings 0",
"changeset_presence": "node scripts/check-changeset-presence.mjs exit 0: '2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/11865-grouping-editor-shared-select.md.'",
"changeset_no_major": "node scripts/check-changeset-no-major.mjs exit 0: 'No changeset declares amajorbump.'",
"new_line_citations": "pnpm check:new-line-citations exit 0: 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only'",
"control_bytes": "pnpm check:control-bytes exit 0: 'check-control-bytes: OK'",
"component_surface_parity": "pnpm check:component-surface-parity exit 0: 'component-surface-parity: REPORT-ONLY -- exit 0'; no row names the grouping editor",
"eager_closure": "apps/console pnpm exec vite build (VERDICT command-exit 0 both legs) + pnpm check:eager-closure (exit 0 both legs). Head b5272f9: 'Console eager closure is 3306.2 KB gzipped across 336 of 2458 chunks (budget: 3307.0 KB, headroom: 0.8 KB).', eagerGzipBytes 3385540. Base (same tree, grouping-editor.tsx at its e81c846 blob d503cdb8, the only bundle input this branch changes): same line, eagerGzipBytes 3385542. Delta -2 gzip bytes, same 336 eager chunks; only raw change ui-components +157 raw / +22 gzip, other chunks move a few gzip bytes with 0 raw bytes (content-hash specifiers). ui/select and @radix-ui/react-select were already eager. Local builds: the absolute differs from the bot's 3306.6 KB on another PR's tree; the delta is this PR's.",
"derived_extra": "all exit 0: node scripts/check-changeset-fixed.mjs, node scripts/check-changeset-overwrite.mjs ('No pre-existing changeset was modified or deleted.'), node scripts/check-changeset-claims.mjs ('No pending changeset names a file this change touches.'), pnpm check:pending-changeset-literals ('No test source names a pending changeset.'), node scripts/check-test-path-roots.mjs (OK), pnpm check:phantom-deps ('Every in-scope import is declared by the package that publishes it.'), pnpm check:unreferenced-sources (OK), node scripts/check-vi-mock-specifiers.mjs (OK)",
"derivation": "objectui has no dispatch-gates.mjs; the list is the dispatch's lead plus gates derived by hand from objectui package.json and .github/workflows/.yml against the actual diff (one components source file, one components test, one changeset). Repo-wide pnpm lint and the full pnpm test farm are CI's."
},
"tests": "New suite packages/components/src/tests/grouping-editor-shared-select-11865.test.tsx, 12 tests: the control is the Radix combobox trigger; picking a field writes {fields:[{field:'status',order:'desc',collapsed:true}]} with JSON.stringify byte-equality; other levels untouched; per-row options in fieldOptions order; re-pick writes nothing; empty state, add, remove-last writes undefined; outside-options field shown and listed first; keyboard open (Enter on trigger) and select (Enter on option). Reverse leg 1 (base component content written into the worktree, implementation committed first, trap restore with git checkout HEAD --): 'Tests 9 failed | 3 passed (12)'; the green three are the empty-state/add/remove pins; the observed direction differed from the first prediction (round-trip pins are red on the native control because they open the primitive's listbox) and the suite header was corrected to the observed one. Reverse leg 2 (uncommitted scratch readings on the base component): outside-options received {tag:'SELECT', value:'name', selectedIndex:0, shown:'Name'} for a level holding 'region'; a native change to 'status' wrote exactly the literal the suite pins (green); per-row options ['Status','Owner'] (green). Both restores proven: hash-object == HEAD blob fe7e8e2c, git diff HEAD 0 bytes, status clean. H4 probe (uncommitted scratch, testing-library role/name queries): base SELECT and head BUTTON both empty-named, 0 comboboxes with a non-empty name.",
"mcp_calls": "0 - no MCP GitHub calls",
"api_writes": "3 - all through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches each): (1) pr_create → POST /repos/objectstack-ai/objectui/pulls (objectui#11934, draft, read-back 8922 bytes sent, 8922 stored, identical); (2) label-write --assign os-bill → POST /repos//issues/11934/assignees (read-back matches); (3) this os-dev-report comment → POST /repos//issues/11865/comments. git push (not REST): the empty branch probe plus three commit pushes.",
"deviations": [
"Tests live in packages/components/src/tests/ (where every components test lives; there is no test file next to custom/.tsx), read as the claim's 'tests beside it'.",
"H3 not pinned: GroupingEditorProps has no disabled/readOnly, neither consumer gates the editor, no ancestor fieldset; adding a channel is a props change, off this claim.",
"H4 not pinned as an accessible-name assertion: there was no name before or after (measured); naming it needs a label string the frozen labels prop does not carry and packages/i18n is off the claim. Keyboard selection is pinned.",
"The base leg of the eager-closure measurement is this tree with grouping-editor.tsx at its e81c846 blob rather than a separate main worktree: it is the only bundle input the branch changes, so the two builds differ in exactly that file.",
"Commit trailers use objectui AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude) over the harness reminder's model-named trailer; the PR body ends with the os-dev session-URL footer, not the harness's footer form. Both by the stated precedence, reported, not silent.",
"No labels written: the dispatch names none and objectui path labels belong to labeler.yml."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed (in the PR's Acceptance notes) - the Group, Filter and Sort panels' field triggers render with no accessible name; measured for the Group panel in happy-dom (old and new control), read from source for Filter and Sort (no aria-label on their SelectTrigger). No public-entry reach measured, so not a filing class. Dedupe words: field picker accessible name · select trigger aria-label list toolbar · group filter sort combobox unnamed",
"carrier: 承接者:无 · noted, not filed (in the PR's Acceptance notes) - Radix SelectItem refuses an empty-string value; ListView keeps a string column as written, so a column authored as an empty string would reach the Group, Filter and Sort pickers' items. Whether the spec refuses an empty column name was not measured: read-only inference."
],
"cleanup": "worktree /home/user/objectui-issue-11865 removed (node_modules deleted first, plain git worktree remove, no --force); scratch PR files deleted; no dev server or background job left running"
}objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsReview: ACCEPT (partial, the Group panel) — PR objectui#11934 ·
domain:uiseat 2 · 2026-10-08T08:55ZImplemented-by:
os-devsubagent ofsession_01MgfduSkFrfM3eorB3UGfAU, branchclaude/issue-11865-grouping-editor-select, headb5272f9.
Reviewed-by:domain:uiseat 2 (session_01MgfduSkFrfM3eorB3UGfAU), against GitHub and the branch diff, not against the report's prose.Checklist:
-
PR shape: draft, base
main, first linePart of #11865. That is correct: the validation editor's and Explain access's selects remain, so the card stays open. No closing keyword sits next to any card.Clause-②: nois at the start of a line. The assignee isos-bill. -
Scope: 3 files, all inside claim
6055510431:packages/components/src/custom/grouping-editor.tsx;- a new test in
packages/components/src/__tests__/, where every components test lives; - the changeset.
GroupingEditorProps, the exports of@object-ui/components,packages/components/src/ui/**andpackages/i18n/**are untouched. -
Triage direction met: each grouping level picks its field with the shared Radix
Select, the control the Filter panel's pickers already use (filter-builder.tsximports the same../ui/select).- Combobox was not used: the Group panel lists only the view's columns, a subset of the Filter list.
- The value written is byte-identical:
onChangereceives the same{ fields: [...] }, pinned withJSON.stringifyequality.
-
One display change, accepted: a level grouped by a field that
fieldOptionsdoes not list now shows that field's name. The native control showed the first option, which was not the field the list was grouped by. The extra item is mounted only then, and an empty value is guarded, becauseSelectItemrefuses"". -
Hypotheses, falsified and accepted:
- Read-only state:
GroupingEditorPropshas nodisabledorreadOnly, and neither consumer gates the editor. Adding one is a props change, which is off this claim. - Accessible name: the field picker had none before and has none after (measured). Naming it needs a label string the frozen
labelsprop does not carry.
- Read-only state:
-
Clause-②: noholds: no export, prop orpackages/i18nkey. -
Changeset:
@object-ui/components: patch. Every sentence was checked against the diff, including "removing the last level still passesundefined" and "the Filter and Sort panels" (both useSelectTrigger). -
Docs: no README or
content/docssentence names the Group panel's control (git greponmain). Nothing to patch. -
Evidence:
- The new suite plus the two
ListView/ViewSettingsPopoverconsumers and the column-identity ratchet pass: 4 files, 170 tests. Bothpackages/componentsshards pass. - The type-check passes.
- Reverse leg: with the base component, 9 of 12 pins fail, and the 3 that stay green are the empty, add and remove pins. The restore was proven.
- The new suite plus the two
-
First-load bytes: −2 gzip bytes against the same tree with the base component, measured with a console build and
check:eager-closure.ui/selectand@radix-ui/react-selectwere already eager. -
CI:
Bundle Analysisis red onb5272f9's merge ref, at 3307.0 KB against the 3307.0 KB ceiling. That ismain's own failure, anchored at objectui#11937:main19a7348fails the same check on push. This PR lands through the merge queue after objectui#11937 makesmaingreen and the check re-runs green on this PR's head.
Out-of-scope notes,
carrier: none:- the Group, Filter and Sort panels' field triggers have no accessible name;
- Radix
SelectItemrefuses an empty-string value, so a column authored as""would reach these pickers. Whether the spec refuses an empty column name was not measured.
Generated by Claude Code
-
objectstack-fleet commented
on Oct 8, 2026 ContributorAuthorMore actionsPartial landing: the Group panel is on
main; the validation editor and Explain access return to the queue ·domain:uiseat 2 · 2026-10-08T11:39ZRelease:
session_01MgfduSkFrfM3eorB3UGfAU(domain:uiseat 2, accountos-bill) closes claim6055510431. That claim covered the list Group panel only.Delivered. PR objectui#11934 (
Part of #11865) merged as7241a81, checked onorigin/mainby content:GroupingEditorpicks each level's field with the sharedSelect, the control the Filter and Sort panels use. The<select>left in the file is in comments only.- The value written is unchanged.
- A level grouped by a field outside the options now shows that field's name.
- The seat's review is
6056362835.
Remaining, for the next claims. The other two surfaces triage named in
6051973479:- The validation editor's native select:
ObjectValidationsPanel.tsx. This seat's re-land of objectui#11931 (claim6057967120on objectui#11861) holds that file now. Claim after it lands. - Explain access:
AccessExplainPanel. Claim after objectui#11862, which holds it, has landed.
Both stay
Clause-②: noas long as no published prop changes.Notes from this slice,
carrier: none:- The Group, Filter and Sort field triggers have no accessible name.
SelectItemrefuses an empty-string value.
Labels:
pm:dispatched→pm:queue. The assignee is cleared.
Generated by Claude Code
91 remaining items
- added 15 commits that reference this issue
on Oct 9, 2026
Filing gate ② — a product decision only the maintainer can make.
Who acts on it: objectui maintainer (components owner) — a decision on the select primitive. ⛔ Not a claim. Filed on the maintainer's word: 「选择框不统一:分组面板、校验规则编辑器、Explain access 用的是浏览器原生 ,其他地方是带搜索的下拉框」. Maintainer quick-read Settle on one select primitive? 47 native <select> elements in 30 source files (tests and comments excluded) sit beside the shared Select and Combobox, so the same kind of control looks and behaves differently from panel to panel. Seen List Group panel (grouping-editor in components): native select, while the Filter panel next to it uses comboboxes. Validation rule editor (ObjectValidationsPanel): Type / Status / Severity are native selects; in a read-only package they look editable (#11781). Explain access (AccessExplainPanel): the object picker is a native select of 24 objects with no search. A NativeSelect wrapper exists in components but has no callers. Options A — shared Select for short fixed lists, Combobox (with search) above a threshold; a lint rule against raw <select> outside the primitive; migrate in passes. B — move only the long-list pickers (objects, fields) to Combobox. C — status quo. Recommendation A, starting with the three surfaces above. Environment Source read on objectui main at 82500a7 and objectstack main at 033e5c53. Live observations are from the 2026-10-07 browser QA pass: examples/app-showcase booted with objectstack dev --ui --seed-admin (objectstack 879bd38c), console from objectui 179f6fe, Chromium 141 at 1440×900, signed in as the seeded platform admin admin@objectos.ai. Duplicate check Semantic issue search on objectstack-ai/objectui (open and closed), native select element inconsistent with searchable combobox Select component styling: 8 hits (0 open, 8 closed). No hit describes this. Positive control in the same run: related lists action id refusal notice shown to end users on objectstack-ai/objectui returned #11768: yes. Dedupe words: native select inconsistent combobox · raw select element studio · explain access object picker no search Filed by Claude Code (session session_01D76mrPJrSSdaKRxR2rvrMG) from the 2026-10-07 QA pass and a follow-up source reading. Generated by Claude Code