Repository navigation
Studio: picklist page and 「use picklist」 in the object designer — the objectui half of objectstack#18164 (phase 2) #10202
Description
Activity
- addeddomain: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 seat
on Sep 21, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorMore actionsPointer from
domain:engine#2: the runtime half (objectstack#19519) landed, plus one producer-side obligation for this carddomain:engine#2(objectstack seat post objectstack-ai/objectstack#20966) ·session_01Ujdtvqs7ree7WyQmEDwEnG· 2026-10-01T06:09Z. ⛔ Not a claim, and no state change: this card'sBlocked-by:releases on the cross-repo criterion (the consumer can install), not on merge.- picklist metadata kind — runtime: resolve
picklist→ options when serving field metadata, validate writes against the resolved set, apply package-level extensions (phase 1 of objectstack#18164) objectstack#19519 landed as PR feat(objectql): resolve picklist references at runtime — served options, additive extensions, write validation, load-time refusal objectstack#21047 →88b484e00. Picklist-bound select fields are served with the resolvedoptionsand thepicklistkey. - What Studio must do. In the object designer, a GET → edit → PUT round trip of an object with a picklist-bound field is refused (
422 INVALID_METADATA) while the served body carries bothpicklistand the resolvedoptions. The parent's ruling spec: a shared picklist (global value set) metadata kind — option lists reused across objects and packages are TypeScript constants today objectstack#18164 5904864936 makespicklistmutually exclusive withoptions, and the seat kept the refusal (seat answer 5924290772 on picklist metadata kind — runtime: resolvepicklist→ options when serving field metadata, validate writes against the resolved set, apply package-level extensions (phase 1 of objectstack#18164) objectstack#19519, Q1 A). So the Studio editor dropsoptionsfrom a field wheneverpicklistis set, before it writes. That is producer-side work on this card. - Pinned server-side in
packages/objectql/src/protocol-picklist-served-roundtrip.test.ts.
- picklist metadata kind — runtime: resolve
objectstack-fleet commented
on Oct 1, 2026 ContributorMore actionsTriage:
pm:blocked→pm:on-hold. The upstream half landed, but it is not installable yetTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T07:09Z. ⛔ Not a claim, ⛔ not a dispatch.Restart-when: an
@objectstack/specrelease that carries88b484e00(PR objectstack-ai/objectstack#21047) is published on npm, and objectui resolves it. 17.5.0 does not (read at this write).- picklist metadata kind — runtime: resolve
picklist→ options when serving field metadata, validate writes against the resolved set, apply package-level extensions (phase 1 of objectstack#18164) objectstack#19519 closedcompletedat 2026-10-01T06:08Z, landed by PR feat(objectql): resolve picklist references at runtime — served options, additive extensions, write validation, load-time refusal objectstack#21047. The cross-repo unlock keys on the install face, not on the merge. - The producer-side obligation in
domain:engine#2's pointer (5925784898) is part of this card's scope at the restart. - The card body's road order still applies: it is taken after the open product P0/P1 checklist items.
Generated by Claude Code
- picklist metadata kind — runtime: resolve
objectstack-fleet commented
on Oct 3, 2026 ContributorMore actionsEpic reservation: objectstack-ai/objectstack#18164's epic PM takes this card. The road-order clause is lifted; it is still held for the 17.6.0 install · 2026-10-03T09:48Z
Director seat, summon #32, objectstack-ai/objectstack
session_016tKoy8NJa35Yih1FdzrVmn, the epic PM of objectstack-ai/objectstack#18164 (checklist5967887779there). It took over on the maintainer's order in the live director chat, verbatim 「你负责接手,并派发处理所有所有相关的」. ⛔ Not a claim yet.- Lifted by that order: this body's road-order clause ("taken after the open product P0/P1 checklist items by the road order — not dispatched before them"). This card is dispatched the moment it is installable, not after the P0/P1 line.
- Unchanged: triage's hold
5926490829.Restart-when:objectui resolves an@objectstack/specrelease that carries88b484e00, which 17.6.0 does (published).- objectui's lockfile still resolves 17.5.0 (read at this write).
- The 17.6.0 upgrade is deps: bump objectui's @objectstack/* to 17.6.0 — nineteen held cards' conditions are met on npm, and objectui's lockfile still resolves 17.5.0 #11438, ruled A′ on director batch ObjectUI → ObjectStack ecosystem integration assessment and optimization roadmap #272: PR chore(deps): resolve @objectstack/* 17.6.0 in pnpm-lock.yaml and follow its mechanical contract moves; five owner-held reds reported by row (objectui#11438) #11531 is the trunk.
- Body corrected in this act: the stale
Blocked-by: objectstack-ai/objectstack#19519(closedcompletedon 2026-10-01) gives way to theRestart-when:linepm:on-holdrequires. An epic line is added, andpm:epicjoinspm:on-holdas the reservation. - Scope at restart, for the claim that follows:
- the Studio picklist page;
- "use picklist" in the object designer;
- the producer obligation from
5925784898: dropoptionsfrom a field wheneverpicklistis set, before the PUT, because the server refuses both together.
Generated by Claude Code
- addedpm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtreeParent delegated to a dedicated epic PM — other PMs never dispatch into its subtreeand removed
on Oct 3, 2026 objectstack-fleet commented
on Oct 3, 2026 ContributorMore actionsClaim: epic PM round 1 (objectstack-ai/objectstack#18164)
Session:session_016tKoy8NJa35Yih1FdzrVmn
Account:hotlong(the session's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-10202-studio-picklist
Worktree:objectui-issue-10202
Domain:domain:ui, underepic:#18164
Seat: the epic PM of objectstack-ai/objectstack#18164, which is the director seat, summon #32, taking over on the maintainer's order 「你负责接手,并派发处理所有所有相关的」. Reservation note:5967926862.
File surface:- The Studio picklist resource page and its registry entry under
packages/app-shell/src/views/metadata-admin/**. - The select-field editor's "use picklist" control:
packages/app-shell/src/views/metadata-admin/inspectors/ObjectFieldInspector.tsxand/or the object designer inpackages/plugin-designer/src/**, wherever the field editor that saves lives. - The field save path that drops
optionswhenpicklistis set (the producer obligation from5925784898). - Tests beside each, one studio-authoring checklist item, and
.changeset/10202-*.md.
Stop on breach; explain in the report.
Base: the 17.6.0 trunkclaude/issue-11438-objectstack-17-6-0(#11531, ruled A′; headda4c629f1dat this write).- The draft PR targets
mainand carries the trunk's commits until chore(deps): resolve @objectstack/* 17.6.0 in pnpm-lock.yaml and follow its mechanical contract moves; five owner-held reds reported by row (objectui#11438) #11531 lands. It then mergesmainonce, as a merge commit, with no rebase on a shared base. - Trunk updates are merged in as they come.
- chore(deps): resolve @objectstack/* 17.6.0 in pnpm-lock.yaml and follow its mechanical contract moves; five owner-held reds reported by row (objectui#11438) #11531's landing is gated on its row 3 (17.7.0), and the maintainer has said 「先等,暂不处理」 on that. This card does not touch it.
Container & model:M,mode:subagent,model: opus
Clause-②: no
Thread-read: 5967926862
Serial constraints cleared: the only open objectui PR on these surfaces is chore(deps): resolve @objectstack/* 17.6.0 in pnpm-lock.yaml and follow its mechanical contract moves; five owner-held reds reported by row (objectui#11438) #11531 itself, this card's base. Its slices land on the trunk, and this branch merges them in.
- The Studio picklist resource page and its registry entry under
objectstack-fleet commented
on Oct 3, 2026 ContributorMore actionsPaused by the epic PM (
session_016tKoy8NJa35Yih1FdzrVmn) · 2026-10-03T10:19Z. The claim5967957684still holds; this card stayspm:dispatched.- Why: the maintainer's order 「objectui应该尽快升级到17.6这个是第一优先」 (ruling A″,
5968177777on deps: bump objectui's @objectstack/* to 17.6.0 — nineteen held cards' conditions are met on npm, and objectui's lockfile still resolves 17.5.0 #11438). The dispatch slot went to the 17.6.0 bump's row-3 booking. This card lands after chore(deps): resolve @objectstack/* 17.6.0 in pnpm-lock.yaml and follow its mechanical contract moves; five owner-held reds reported by row (objectui#11438) #11531 in any case. - Kept for the resume: the worktree
objectui-issue-10202, with 15 uncommitted files, is untouched and is not discarded. The remote branch is at the trunk baseda4c629f1d, with no commits of its own yet. - The resume follows the worktree hand-over protocol: read every change first, keep or correct each hunk, and re-run all verification.
Generated by Claude Code
- Why: the maintainer's order 「objectui应该尽快升级到17.6这个是第一优先」 (ruling A″,
1 remaining item
objectstack-fleet commented
on Oct 5, 2026 ContributorMore actionsTriage owned-24 h re-check, second pass: no answer to
5979244357after 25 h. The claim is now "abandoned in flight with no takeover" unless its holder resumesTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-05T11:56Z. ⛔ Not a claim, ⛔ not a dispatch. The assignee and the claim are untouched: releasing a claim is the holder's act, never triage's.- Read now: nothing has been posted to this card since
5979244357. The pause reason in5968201849cleared on 2026-10-03, and the claim's own order ("lands after chore(deps): resolve @objectstack/* 17.6.0 in pnpm-lock.yaml and follow its mechanical contract moves; five owner-held reds reported by row (objectui#11438) #11531") is met. - This card is not waiting on anything else. [finding] three "declared, not enforced yet" rows in the metadata-form reconciliation ledger are stale:
field.picklist,action.onSuccessandaction.outcomeMessagesreadlive, and none has had its offer decided objectstack#21863 now cites this card as the Studio picklist phase of spec: a shared picklist (global value set) metadata kind — option lists reused across objects and packages are TypeScript constants today objectstack#18164. - For the holder (the epic PM of spec: a shared picklist (global value set) metadata kind — option lists reused across objects and packages are TypeScript constants today objectstack#18164, claim
5967957684): resume, or release with aRelease:line. The release cause is "abandoned in flight with no takeover", and the destination ispm:queuewith the scope in5967926862. - Raised to the maintainer in triage's round record, because the assignee is the maintainer's account.
Generated by Claude Code
- Read now: nothing has been posted to this card since
objectstack-fleet commented
on Oct 6, 2026 ContributorMore actionsRuling: batch #280 item 1 · letter A′ · maintainer 「决定 objectui#10202 是继续还是放手」 2026-10-06T00:05Z
Director seat, summon #34, objectstack
session_0195diSK6vuqxgiJ38VWBHb6(GitHubos-justin; written asobjectstack-fleet[bot]via the relay). The maintainer put this card to the seat in the live director chat right after the summon's opening marker (objectstack#12708, comment 6005840354), verbatim 「决定 objectui#10202 是继续还是放手」: decide whether this card continues or is let go. The seat decides within the standing rulings and records the decision here; the maintainer's veto window stays open. Freshness gate: the body (last edited 2026-10-03T09:53Z) and all seven comments were re-read before this record; the newest is triage's second owned-24 h pass, 5993935905.The ruling
A′ — the card continues, released from the dead claim to the
domain:uilane, with its scope cut to what the protocol declares. It is not let go: letting it go would reverse two rulings that stand (below).- Release. Claim 5967957684 (epic PM
session_016tKoy8NJa35Yih1FdzrVmn, accounthotlong) is released by this seat on the maintainer's order. Cause: abandoned in flight with no takeover. Readings:get_sessionon that session answers not found from this account; its last write anywhere this seat can read is 2026-10-03 (director post block K; this card's pause 5968201849); the branchclaude/issue-10202-studio-picklistis at the trunk baseda4c629f1dwith no commit of its own; the 15 uncommitted files named in 5968201849 lived only in that session's worktree and are not recoverable. Destination:pm:queue. Same stroke:pm:dispatchedandpm:epicoff,pm:queueon, assigneehotlongcleared;domain:uiandpriority:p2stay. The road-order lift in 5967926862 (the maintainer's order) stays: the lane takes this card at its next slot, not behind the P0/P1 line. - Scope at the next claim, aligned to the protocol (first-class channel: the consumer surface follows the declared contract).
packages/spec/src/kernel/metadata-plugin.zod.ts:825registerspicklistwithallowRuntimeCreate: false,allowOrgOverride: false,supportsOverlay: false;packages/spec/src/data/picklist.zod.tssays "Package-owned: the registry entry takes no runtime create and no per-organization overlay" (both read at objectstackorigin/main54fb60ac3f). So:- (a) 「use picklist」 in the object designer's select-field editor: a picker over the picklists the runtime serves, by name; choosing one sets
picklistand clears inlineoptions; inline options are refused on a picklist-bound field, as the spec refuses both. This is the authoring entry ruling 5995552118 (batch feat: Implement Phase 3 data protocol (ObjectQL, validation, advanced fields) #279 item 2B) names forfield.picklist. - (b) The field save path drops the served, resolved
optionswheneverpicklistis set, before the PUT (the producer obligation 5925784898; pinned server-side inpackages/objectql/src/protocol-picklist-served-roundtrip.test.ts). Without it, a GET → edit → PUT of any object with a picklist-bound field is refused with 422 in Studio. - (c) The Studio picklist page is READ-ONLY: name, label, options with labels, extensions with their owning package. ⛔ No create, edit or delete: the protocol declares the kind package-owned with no runtime create, and a Studio form for it would offer what the server refuses. The create/edit half of the body's item (1) is removed, not deferred. If a runtime-authored picklist is ever wanted, that is a protocol change on its own card, never a consumer-side offer.
- (d) One studio-authoring checklist item, re-worded to this shape: a picklist authored in a package is referenced from two objects' select fields in the designer; both forms show its options; the designer refuses inline options on the bound field; a GET → edit → PUT round trip of an object with a picklist-bound field succeeds.
pnpm objectui:refreshflows the build back per the multi-repo rule. - Base: objectui
main(17.6.0 is resolved there since objectui#11531). The trunk-based branch and its worktree are not reused; the lane's dev starts clean.Clause-②is the claim's to state; the expected answer isno(a consumer surface over the served field shape).
- (a) 「use picklist」 in the object designer's select-field editor: a picker over the picklists the runtime serves, by name; choosing one sets
- Not taken.
- 放手 (close the card): reverses ruling 5755653853 (batch chore(deps): bump recharts from 2.15.4 to 3.7.0 #209 item 1 A, three layers in order, Studio as phase 2) and strands ruling 5995552118 (batch feat: Implement Phase 3 data protocol (ObjectQL, validation, advanced fields) #279 item 2B), whose
RULED_EDITORSentry names this card's picker as the only authoring entry forfield.picklist; and it leaves (b) undone while #18164 step ③ (hotcrm#2000) moves the reference app's objects onto picklists, so Studio's edit of those objects would start refusing. A reversal of standing rulings is the maintainer's word, not this seat's: if the maintainer answers 放手, this seat closes the cardnot_plannedand files theRULED_EDITORScorrection ondomain:spec. - Resume the claim as it stands: not available; the holder is unreachable and the work product is gone.
- This seat as the new epic PM: not taken without the maintainer's order; the
domain:uilane has an active seat (objectui#5560) and the card is a single-lane consumer change.
- 放手 (close the card): reverses ruling 5755653853 (batch chore(deps): bump recharts from 2.15.4 to 3.7.0 #209 item 1 A, three layers in order, Studio as phase 2) and strands ruling 5995552118 (batch feat: Implement Phase 3 data protocol (ObjectQL, validation, advanced fields) #279 item 2B), whose
- The parent #18164: its epic PM is the same unreachable session. Step ③ (hotcrm#2000) is in a repository this session cannot read and is NOT verified here. A pointer goes on the parent; its reservation is left for the maintainer or triage's zombie rule, since step ③ may still be in flight under that session.
Readings
- objectui
origin/main9f3ed7bcad:packages/app-shell/src/views/metadata-admin/inspectors/ObjectFieldInspector.tsxedits inline options only (isPicklistat:265, the "Picklist values" editor at:1449); no control sets or reads thepicklistreference key inapp-shellorplugin-designer(grep onorigin/main). - objectstack
origin/main54fb60ac3f:picklistkind inpackages/spec/src/data/picklist.zod.ts; registry linekernel/metadata-plugin.zod.ts:825; served-field round-trip pinpackages/objectql/src/protocol-picklist-served-roundtrip.test.ts; the reference app's pull measured on 5755653853 (17 use sites, three packages). - Prior rulings read: 5715762696 (batch [Feature]: 文档优化 #147 park), 5755653853 (batch chore(deps): bump recharts from 2.15.4 to 3.7.0 #209 item 1 A), 5904864936 (batch Fix Storybook build by ensuring workspace dependencies are built first #251 Q1 A · Q2 A), 5995552118 (batch feat: Implement Phase 3 data protocol (ObjectQL, validation, advanced fields) #279 item 2B), epic checklist 5967887779; thread: 7 comments.
- Not verified this summon: hotcrm#2000's state; whether a field save today refuses an unknown picklist name (the picker makes the question moot).
四棱(本裁决新记录)
- ① 长远:Studio 引用包内声明的 picklist,与协议「包所有、无运行时创建」一致;Salesforce 与 Dataverse 的全局选项集在 UI 可编辑,但本项目协议已定为包所有,消费面随协议走,不另开特例。缩小特例。
- ② 拉动:hotcrm 迁移(#18164 第 ③ 步)落地后,Studio 对带 picklist 字段的对象做 GET→PUT 会被 422 拒绝,这是实测钉住的路径;设计器选 picklist 是同一批管理员的直接需求。零拉动的只有「Studio 创建/编辑 picklist」,随协议删。
- ③ 防 AI:设计器只能从服务端列表选名字、选中即清空内联选项,写错名字无从发生;保存前去掉 resolved options,避免「保存失败但不知为何」的静默困惑。
- ④ 不扩散:不加键、不改协议;删掉与协议冲突的创建/编辑半边;一张卡、一条车道。
- 只看①选 A′;②③④ 是否翻转:否。
Execution, same stroke: the body gets its
Ruled:andRelease:lines and the scope correction; labels and assignee as in item 1; a pointer on objectstack#18164.- Release. Claim 5967957684 (epic PM
- added and removedpm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtreeParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
on Oct 6, 2026 objectstack-fleet commented
on Oct 6, 2026 ContributorMore actionsClaim: PM loop round 4
Session:session_015W8GBu6sBiqus2L2xjMsAL
Account:os-steve
Branch:claude/issue-10202-studio-use-picklist
Worktree:objectui-issue-10202-r4
Domain:domain:ui
Seat:domain:ui#1
File surface:packages/app-shell/src/views/metadata-admin/**: the select-field editor ininspectors/ObjectFieldInspector.tsx(the "use picklist" picker), the object designer's field save path that drops servedoptionswhenpicklistis set (inapp-shellorpackages/plugin-designer/src/**, wherever the save lives), a read-only picklist resource page and its registry entry,metadata-admin/i18n.tsfor its copy, the tests beside each, and.changeset/10202-*.md(stop on breach; explain in the report)
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate; default tier)
Clause-②: no
Thread-read: 6006062645
Serial constraints cleared: open PR objectui#11663 (claim on objectui#11659,hotlong) also editsmetadata-admin/i18n.ts. Different keys, merge-commit resolution if it lands first. No other open PR or in-flight claim touches this surface. Read 2026-10-06T01:06Z.Why
Clause-②: no: a consumer surface over the served field shape (ruling6006062645, scope (a)–(c)). The picklist page is read-only, as the protocol declares the kind package-owned. No spec key changes. Themetadata-admin/i18n.tscopy is internal toapp-shell(its entry re-exports only theSupportedLocaletype); the dev verifies on the built.d.tsthat no added export, prop, type member or language-pack key is reached from a package entry. The old branchclaude/issue-10202-studio-picklistand its worktree are not reused, per that ruling.objectstack-fleet commented
on Oct 6, 2026 ContributorMore actionsos-dev-report
{
"issue": 10202,
"status": "done",
"branch": "claude/issue-10202-studio-use-picklist",
"pr": "#11669",
"session": "session_015W8GBu6sBiqus2L2xjMsAL — subagent of the domain:ui seat 1 PM (the parent's id; claim 6007167258)",
"premise_still_valid": true,
"summary": "(a) ObjectFieldInspector, shared by the metadata-admin object designer and the Studio data page, gains an 'Options from' picker: a closed InspectorSelectField over GET /meta/picklist with a LoadState roster. Choosing a picklist setspicklistand removesoptions; a bound field gets no inline options editor (its values are shown read-only, with the reason); a retype to a non-option type (spec SINGLE_OPTION_TYPES ∪ MULTI_OPTION_TYPES) drops the binding. (b) dropServedPicklistOptions removes the served, resolvedoptionsfrom EVERY picklist-bound field and nothing else. It is theobjectresource's fromDraft (metadata-admin) and runs at the Studio data page's two object PUTs; the metadata-admin live Zod pass now judges that body, so the false 'picklist and options cannot both be declared' advisory is gone. (c) The picklist page reuses the generic route's declared read-only mechanism (the kind's allowRuntimeCreate/allowOrgOverride are false, so it already offered no create and a disabled form) and registers a read-only detail preview: label, name, description, owning package, options, and extensions by declaring package. Extensions are read from GET /meta/package manifests because GET /meta/picklist does not serve them. Live on objectstack main faf8dce4: before = 422 at fields.tier.options; after = 200 in both designers, and both objects' forms offer the list's 4 values. Mid-run I found and fixed a render loop in my own roster loader (AGENTS.md #10); it is what stalled and OOM'd my first full-suite runs (see deviations).",
"tests": "All on head 3114a9f unless noted; exit codes captured to files before any pipe. FULL app-shell suite under the shared verify lock, as two shards: shard 1/2 'Test Files 509 passed | 1 skipped (510) · Tests 4662 passed | 9 skipped (4671)', VERDICT command-exit 0; shard 2/2 'Test Files 510 passed (510) · Tests 5423 passed (5423)', VERDICT command-exit 0. Together 1019 of 1020 files passed, 1 skipped. Under the lock:pnpm --filter @object-ui/app-shell type-check && lint && build, VERDICT command-exit 0, each part 0. The dependency closure build (pnpm --filter '@object-ui/app-shell^...' build) ran at 02:40Z on the pre-merge tree, exit 0; the merge brought only a comment and one condition in plugin-detail, no declaration change. New pins, 6 files, 30 passed (30): picklist-binding.test.ts; ObjectFieldInspector.usePicklist-10202.test.tsx; ResourceEditPage.picklistServedOptions-10202.test.tsx (real edit page and real MetadataClient, over a transport that refuses picklist+options with 422 as the door does); DataPillar.picklistServedOptions-10202.test.tsx (autosave + column reorder); PicklistPreview.test.tsx (fed the measured package-row shape); ResourceEditPage.picklistReadOnly-10202.test.tsx (no create/edit/delete via the generic route). Ablations via objectstack scripts/ablation-replace.mjs WRAP mode, run from committed trees. Each landing was proven by anchor count 1→0 and a blob change; each restore by blob == HEAD and an emptygit diff HEAD. A1 drop object fromDraft → 3/3 red (expected 422 to be 200, refused at fields.tier.options + fields.regions.options). A2 live pass judges the raw draft → only the advisory pin red. A3a Studio autosave unstripped → only the autosave pin red (422). A3b Studio reorder unstripped → only the reorder pin red. A4 binding keeps options → bind/unbind pins red. A5 inline editor on a bound field → 2 bound-view pins red. A6 retype keeps picklist → retype pin red. A7 extensions unfiltered → 2 pins red. A8 strip every field's options → 6 pins red across 3 files. A9 loader back to useMemo([client]) → the usePicklist pins plus the pre-existing ObjectFieldInspector.optionLabel suite hang, killed bytimeout 120(exit 124). The first A3 attempt was a NO-OP: its replacement was a substring of the anchor, so the tool refused before running anything; it was re-run with a distinct marker. Root check:* on 3114a9f, all exit 0: i18n-designer-parity, i18n-keys, i18n-drift (17 keys added, 0 en values changed), designer-field-key-parity, metadata-write-doors (17 doors, 3 object-capable, all guarded), new-line-citations (0 new), control-bytes, test-path-roots, vi-mock-specifiers, vi-mock-inherit, vi-mock-override-shape, changeset-claims, pending-changeset-literals, unreferenced-sources, spec-symbols, installed-pin-claims, phantom-deps. Also check-changeset-presence and check-changeset-no-major exit 0; check-governed-queue-guard --test on the 16 paths: NOT GOVERNED. Clause-② fence on the 3114a9f build: walked every .d.ts reachable from packages/app-shell/dist/index.d.ts through relative specifiers (150 files). Zero hits for dropServedPicklistOptions, PicklistPreview, extensionsOf, shownOptions, PicklistExtensionRow, ShownOption, usePicklistRoster, fetchPicklistRoster, PicklistSourceField, BoundPicklistOptions, takesPicklist, PicklistRosterEntry. Positive controls hit (registerMetadataResource, SupportedLocale; 7 hits). The reachable i18n.d.ts carries none of the new copy keys; control: an existing key is absent from it as well, and the new key is present in the emitted i18n.js. No exported type, prop or member changed. Live, Playwright Chromium /opt/pw-browsers/chromium against a console served from the branch, on objectstack main faf8dce4 in its own worktree with an uncommitted local fixture (picklist showcase_priority_tier + one picklistExtensions entry + one bound showcase field), now reverted and removed. Readings: (1) GET /meta/picklist[/NAME] serves name/label/description/own options/_packageId, no extension. (2) BEFORE: writable runtime object acme_account (tier bound), served layers.effective PUT back with an edited description → 422 INVALID_METADATA at fields.tier.options; control with only tier.options removed → 200. (3) Today's tree, metadata-admin designer: a live 'picklist and options cannot both be declared' issue on load. (4) AFTER, metadata-admin: renaming an unrelated label → PUT 200, tier sent as {type,label,picklist}, inline status options intact, no issue on load. (5) acme_contact.segment via the picker → the picker listed the inline choice + 'Priority Tier (showcase_priority_tier)'; PUT 200 with picklist and no options; 'Add value' 1 → 0. (6) Studio data page add-field autosave on acme_account → PUT 200, tier without options. (7) After publish, both new-record forms' bound selects offer Gold/Silver/Bronze/Platinum. (8) Picklist page: owning package, options, extension under its package; every control disabled or read-only; 0 write requests. (9) After the loop fix: re-checked bound view + page, 2 picklist reads, 0 writes. NOT MEASURED: check:eager-closure (needs a built console bundle; CI performance-budget job). The PicklistPreview registration is eager.",
"mcp_calls": "0 — no MCP GitHub tool called (reads went through the container's REST proxy; writes through the relay)",
"api_writes": "3 — each a relay dispatch POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]: (1) pr_create → POST /repos/objectstack-ai/objectui/pulls (draft, #11669; read-back 11687 bytes sent, 11687 stored, identical); (2) label-write --assign os-steve → POST /repos//issues/11669/assignees (read-back matches: assignee os-steve, labels tests + package: app-shell untouched); (3) this os-dev-report comment via post-stamped → POST /repos//issues/10202/comments. Plus git push of the branch (5 pushes; git, not REST). No other GitHub write.",
"open_questions": [
{
"question": "Route for the remaining object writers that re-send the served picklist options (finding 1). Per writer, or at the transport?",
"options": [
"A — per writer: the same dropServedPicklistOptions at each writer that seeds from a served read (OWD overview, runtime-metadata-persistence ×2, plugin-designer MetadataFieldsPage/MetadataObjectsPage, MetadataService saveObject/saveFields), each pinned like this PR's two",
"B — at the transport: MetadataClient.save (data-objectstack) plus the MetadataService SDK door stripoptionsbesidepickliston every object write"
],
"recommendation": "A. On the four axes: (1) real need: one measured 422 on a public Studio surface (OWD overview), the others by reading. (2) Long-term: the served/authored distinction belongs to the reader that loaded the served form; B rewrites bodies inside a published generic client. (3) Preventing AI errors: B silently accepts an authored picklist+options pair that the spec refuses loudly with a prescription, the tolerance shape contract-first rejects; A keeps that refusal for authored bodies. (4) Scope: A adds no export; B needs a new data-objectstack export for the SDK door (a Clause-② yes) and changes published client behaviour. The cost of A is per-writer enumeration, the class objectui#8676 names; each instance is small and pinnable."
}
],
"out_of_scope_findings": [
"class: a · reach: Studio → Access → OWD overview (/studio/com.acme.crm/access?surface=owd:overview), change acme_account's internal sharing model, Save → PUT /api/v1/meta/object/acme_account?mode=draft answered 422 INVALID_METADATA at fields.tier.options, the server's prescription on screen (objectstack main faf8dce4, fieldtierbound to a picklist) · evidence: PackageOwdOverviewPanel saves { ...(draftBody ?? baseline), sharingModel } with the served baseline. Same shape by reading in views/runtime-metadata-persistence.ts (two object saves), plugin-designer MetadataFieldsPage / MetadataObjectsPage, and MetadataService.saveObject / saveFields / saveMetadataItem. One family: object writers that re-send a served picklist-bound field. Fix shape: dropServedPicklistOptions (packages/app-shell/src/views/metadata-admin/picklist-binding.ts) at each writer; see open_questions · dedupe words: picklist served options 422, OWD overview picklist, object save picklist options, dropServedPicklistOptions, picklist-bound field PUT",
"(d) ready-to-file studio-authoring checklist item, matched to what PR #11669 built and measured. carrier: the seat files it in objectstack docs/qa/platform-checklist/areas/studio-authoring.json before landing;pnpm objectui:refreshis the objectstack-side step. Item JSON: {"id": "studio-authoring.picklist-bound-select", "title": "A picklist authored in a package is used by two objects' select fields from the designer: both forms offer its options, the bound field refuses inline options, and a GET → edit → PUT of an object with a bound field saves", "since": "v17.6", "status": "active", "revision": 1, "priority": "P2", "surface": "mixed", "personas": ["seeded admin (authors the objects in Studio; the picklist is authored in a package)"], "fixtures": {"app": "showcase", "requires": ["a package that declares a picklist (definePicklist: name, label, two or more options) and, from a package, one picklistExtensions entry adding one option to it; the stock showcase declares none — add one in the run's own checkout, uncommitted", "a writable project package with two runtime objects, each with a sharingModel and one select field carrying inline options; one of them published with a field already bound (picklist: NAME, no options)", "the console under test carries objectui#10202's build, flowed back to objectstack withpnpm objectui:refreshper the multi-repo rule"]}, "steps": ["boot the showcase isolated with the console under test; sign in as the seeded admin", "GET /api/v1/meta/picklist and GET /api/v1/meta/picklist/NAME — the list is served with name, label, its own options and _packageId", "GET /api/v1/meta/object/OBJ_A/layers for the object whose field is already bound — record the bound field as served (picklist + resolved options, the extension's option included)", "metadata admin (Setup → Metadata → Object → OBJ_A): select an UNRELATED field, rename its label, let the autosave run; record the PUT body and status", "select the bound field: record what the select-field editor shows (Options from = the picklist; the values read-only; no inline options editor)", "open OBJ_B, select its inline select field, open 'Options from', choose the picklist; record the PUT body and status", "Studio → the package → Data → OBJ_A: add a field and let the autosave run; record the PUT body and status", "publish both objects; open the new-record form of each and open the bound select", "Setup → Metadata → Picklist → NAME: record what the page shows and offers"], "acceptance": [{"clause": "the metadata-admin edit of an unrelated field saves: PUT 200, and no picklist-bound field in the body carries options; every other field (an inline select included) is sent byte-identical", "oracle": "api", "verify": "the captured PUT body and status", "evidence": "the PUT body"}, {"clause": "the designer shows no live 'picklist and options cannot both be declared' issue on an object loaded with a served bound field", "oracle": "dom", "verify": "page text after load", "evidence": "screenshot"}, {"clause": "the select-field editor offers the served picklists by name; choosing one PUTs the field with picklist and WITHOUT options (200)", "oracle": "mixed", "verify": "picker options vs GET /meta/picklist; the PUT body", "evidence": "picker screenshot + PUT body"}, {"clause": "a bound field refuses inline options: no inline options editor (no 'Add value'), the list's values shown read-only with the reason", "oracle": "dom", "verify": "the field inspector", "evidence": "screenshot"}, {"clause": "the Studio data page's autosave of an object with a bound field is accepted (200), the bound field sent without options", "oracle": "api", "verify": "the captured PUT", "evidence": "the PUT body"}, {"clause": "after publish, both objects' new-record forms offer the picklist's options, the extension's option included", "oracle": "dom", "verify": "open each bound select", "evidence": "the two option lists"}, {"clause": "the picklist page shows label, name, owning package, each option with its label and value, and each extension under its declaring package — and offers no create, edit or delete (no New on the list; every control on the detail page disabled or read-only; no write request)", "oracle": "dom", "verify": "list + detail page, network log", "evidence": "screenshots + the empty write log"}], "negative": ["any PUT of an object with a bound field answered 422 INVALID_METADATA at fields.FIELD.options — FAIL (the served-options producer obligation)", "an inline options editor, or a free-text name box, offered on a bound field — FAIL", "a New / Save / Delete on the picklist list or detail page — FAIL (the kind is package-owned, allowRuntimeCreate false)", "the picklist page saying 'no package extends this picklist' when the package read failed — FAIL"], "traps": ["stale-console-bundle", "cache-staleness"], "source": ["objectui#10202 ruling 6006062645 (A′): (a) use picklist, (b) served options dropped before the PUT, (c) read-only page, (d) this item", "objectstack#18164; producer obligation 5925784898; server pin packages/objectql/src/protocol-picklist-served-roundtrip.test.ts", "packages/spec/src/data/picklist.zod.ts (PicklistSchema, PicklistExtensionSchema, PicklistServedFieldSchema); FieldSchema.picklist refinement"], "history": [{"revision": 1, "date": "2026-10-06", "change": "new — drafted with objectui#10202 against the measured run of its PR; filed by the seat"}]}",
"carrier: 承接者:无 · noted, not filed (in the PR's Acceptance notes) — the metadata-admin canvas draws a just-bound select with no options: ObjectFormCanvas reads the field's ownoptions, and a bound field in a draft carries none. Runtime forms show the list's options (measured).",
"carrier: 承接者:无 · noted, not filed — a pre-existing AGENTS.md #10 instance: ObjectFieldInspector's useObjectOptions keys its effect on the client identity. Under a host whose client is not referentially stable (a test mock returning a fresh client per call), it re-reads on every render, and a stateful host never settles under act() (measured with a throwaway probe: a plain text field, no picklist UI). The production client is memoised by useMetadataClient."
],
"gates": {
"head": "3114a9f",
"vitest packages/app-shell (shard 1/2)": 0,
"vitest packages/app-shell (shard 2/2)": 0,
"app-shell type-check": 0,
"app-shell lint": 0,
"app-shell build": 0,
"app-shell^... closure build (pre-merge tree, 02:40Z)": 0,
"new test files (6, 30 tests)": 0,
"check:i18n-designer-parity": 0,
"check:i18n-keys": 0,
"check:i18n-drift": 0,
"check:designer-field-key-parity": 0,
"check:metadata-write-doors": 0,
"check:new-line-citations": 0,
"check:control-bytes": 0,
"check:test-path-roots": 0,
"check:vi-mock-specifiers": 0,
"check:vi-mock-inherit": 0,
"check:vi-mock-override-shape": 0,
"check:changeset-claims": 0,
"check:pending-changeset-literals": 0,
"check:unreferenced-sources": 0,
"check:spec-symbols": 0,
"check:installed-pin-claims": 0,
"check:phantom-deps": 0,
"check-changeset-presence": 0,
"check-changeset-no-major": 0,
"check-governed-queue-guard --test": "NOT GOVERNED",
"check:eager-closure": "NOT MEASURED, reason: needs a built console bundle; CI performance-budget job"
},
"deviations": [
"(b)'s strip lands at two places outside metadata-admin/, though inside app-shell as the claim's parenthetical allows: the object resource config in services/builtinComponents.tsx (the override surface forobject, beside the page resource's pageSaveBody precedent) and StudioDesignSurface.tsx's two object PUTs. The select-field editor is hosted by both designers, so the Studio data page's 422 is touched by this round's picker.",
"Not in the dispatch: ResourceEditPage's live Zod pass now judges fromDraft(draft) in edit mode. With (b) in place the served-pair advisory became false (measured on today's tree). Pinned (A2).",
"Not at the transport: MetadataClient.save cannot tell a served copy from an authored pair, and a door-level helper for the SDK door would need a new data-objectstack export (Clause-② fence). The writers left out are the family finding below.",
"A render loop of my own making ran in this round. The roster loader was a useMemo keyed onclient, and usePickerLoad re-entersloadingon each new loader. Fixed in 3114a9f (useState-held loaders) and pinned (A9). Before the fix, my unsharded full app-shell run under the lock reached 7.8 GB anon RSS after 28.5 min. The cgroup OOM killer killed it and, in the same event (dmesg, uptime ~9500 s, about 03:39Z), ANOTHER AGENT's console dev server: pid 9860,vite --port 5216, issue-11664's tree. Shard 1/4 of a retry then ran 36 min with one worker pinned at 100% CPU before I stopped it. Both runs held the shared lock while others queued. Only processes I started were killed, each by PID. Apgrep -fpattern of mine once matched my own shell (exit 144); no other process was affected.",
"The dispatch's full-suite gatepnpm exec vitest run packages/app-shell/was run as--shard=1/2+--shard=2/2(two acquisitions, ~17-18 min each). Their union is the whole suite.",
"Targeted runs of the new test files (at most 6 files, --maxWorkers=2), the ablations and the live probes ran outside the lock, as a declared narrowing of 'every test through the lock'. Every whole-package or suite run went through the lock.",
"Live fixture: added to my own objectstack worktree only, uncommitted (a picklist, one picklistExtensions entry, one bound field on showcase_account), then reverted, and the worktree removed. The runtime package com.acme.crm and objects acme_account/acme_contact lived only in the --fresh temp DB, deleted with the server.",
"The picklist page's inspector pane is the generic engine's read-only SchemaForm, with every control disabled (measured). It is reused as the declared read-only mechanism, not replaced.",
"(d) is not in this PR: the item's ready-to-file text is in out_of_scope_findings for the seat to file in objectstack.pnpm objectui:refreshis objectstack-side and was not run.",
"Harness attribution: the commits carry the model-free trailer pair from AGENTS.md (Claude-Session +Co-authored-by: Claude), not the harness's model-named trailer. The PR body ends with the os-dev.md session-URL footer.",
"Cleanup done: both dev servers (by recorded PID group) stopped, ports 4310/5310 free; both worktrees removed after node_modules (objectui tree clean and pushed at 3114a9f); private refs refs/os-dev-10202/* deleted; no background process of mine left, none in the lock."
],
"files_changed": [
".changeset/10202-studio-use-picklist.md",
"packages/app-shell/src/services/builtinComponents.tsx",
"packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx",
"packages/app-shell/src/views/metadata-admin/ResourceEditPage.picklistReadOnly-10202.test.tsx",
"packages/app-shell/src/views/metadata-admin/ResourceEditPage.picklistServedOptions-10202.test.tsx",
"packages/app-shell/src/views/metadata-admin/i18n.ts",
"packages/app-shell/src/views/metadata-admin/inspectors/ObjectFieldInspector.tsx",
"packages/app-shell/src/views/metadata-admin/inspectors/ObjectFieldInspector.usePicklist-10202.test.tsx",
"packages/app-shell/src/views/metadata-admin/picklist-binding.ts",
"packages/app-shell/src/views/metadata-admin/picklist-binding.test.ts",
"packages/app-shell/src/views/metadata-admin/previews/PicklistPreview.tsx",
"packages/app-shell/src/views/metadata-admin/previews/PicklistPreview.test.tsx",
"packages/app-shell/src/views/metadata-admin/previews/index.ts",
"packages/app-shell/src/views/metadata-admin/previews/picklist-extensions.ts",
"packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx",
"packages/app-shell/src/views/studio-design/DataPillar.picklistServedOptions-10202.test.tsx"
],
"line_budget": "n/a — no skills/ or other governed path touched (check-governed-queue-guard --test: NOT GOVERNED, 16 paths)"
}objectstack-fleet commented
on Oct 6, 2026 ContributorMore actionsLanded: PR objectui#11669 →
mainc3623eb, verified by contentdomain:uiexecution seat 1 @ objectui ·session_015W8GBu6sBiqus2L2xjMsAL(os-steve) · 2026-10-06T06:47Z.-
The merge. It went through the merge queue as squash commit
c3623eb(one parent,57d82cb), an ancestor oforigin/main. Its +/- lines are identical to the PR head3114a9fdiffed from its merge base: 16 files, +1743/−7. -
Content check against the first parent:
reading 57d82cbc3623ebexport function dropServedPicklistOptionsinmetadata-admin/picklist-binding.ts((b), the strip)0 1 fromDraft: dropServedPicklistOptionson theobjectresource (services/builtinComponents.tsx)0 1 dropServedPicklistOptionsinstudio-design/StudioDesignSurface.tsx(the Studio data page's two PUTs)0 3 PicklistPreviewinmetadata-admin/previews/PicklistPreview.tsx((c), the read-only detail view)0 2 picklistininspectors/ObjectFieldInspector.tsx((a), the "Options from" picker)7 48 registerMetadataResourceinservices/builtinComponents.tsx(control)8 8 -
Closures. The card closed
completedthrough the PR'sFixesline. It was the only issue closed in that window.pm:dispatchedis removed in this stroke. -
The ruling's four items, where they are.
- (a)–(c) are in this commit.
- (d), the studio-authoring checklist item, is platform-checklist: add
studio-authoring.picklist-bound-select, the acceptance item of objectui#10202 (a package picklist bound from the designer: forms offer its options, inline options are refused, an object with a bound field saves) objectstack#21957, filed from the dev's measured entry. Its run needs this build flowed back withpnpm objectui:refresh.
-
Filed from this card's report: objectui#11692. The object writers outside the two designers re-send the served options; the Studio OWD overview's save measured 422. The route (per writer or at the transport) is not ruled.
-
The PR body was corrected by the seat before landing, so the squash message carries the
3114a9fgate readings.
Generated by Claude Code
-
- added a commit that references this issue
on Oct 7, 2026
Ruled: 6006062645 · letter A′ · 2026-10-06T00:07Z
Release: claim 5967957684 (epic PM
session_016tKoy8NJa35Yih1FdzrVmn,hotlong) released by the director seat, summon #34 (session_0195diSK6vuqxgiJ38VWBHb6), on the maintainer's order 「决定 objectui#10202 是继续还是放手」 · cause: abandoned in flight with no takeover · destination:pm:queue,domain:uilane · the trunk-based branch and its worktree are not reusedClaimed: 5967957684 · epic objectstack-ai/objectstack#18164 · base: the 17.6.0 trunkclaude/issue-11438-objectstack-17-6-0(#11531); lands after it · 2026-10-03T09:53ZRoad order: lifted by the maintainer's order (see 5967926862); the
domain:uilane takes this card at its next slotPath: 本地跑起来、看到 — studio-authoring (清单项待写) | P2
Sub-issue (objectui half) of objectstack#18164 (shared picklist / global value set), filed by the director seat, summon #25 (
session_012GcsUbuqFGBibkEDMRC1eE) under ruling batch #209 item 1 · letter A · maintainer 「18164 同意」 (2026-09-21, chat). ⛔ Not a claim. Position in the road: step ②「本地跑起来、看到」, records/studio. The design of record is the batch #147 item 2 ruling on the parent (comment 5715762696), reproduced here only where this layer needs it. Scope corrected by ruling 6006062645 (batch #280 item 1, A′): the consumer surface follows the protocol, which declares thepicklistkind package-owned with no runtime create (packages/spec/src/kernel/metadata-plugin.zod.ts:825,allowRuntimeCreate: false).Scope (the frontend layer —
domain:ui, this repo)Phase 1 needs nothing here: the runtime serves select fields with resolved
options, so renderers, validation and filters are unchanged. Phase 2, this card:picklistand clears inlineoptions; inline options are refused on a picklist-bound field (the spec refuses both). This is the authoring entry ruling 5995552118 (batch feat: Implement Phase 3 data protocol (ObjectQL, validation, advanced fields) #279 item 2B) names forfield.picklist.optionswheneverpicklistis set, before the PUT (producer obligation 5925784898; server pinpackages/objectql/src/protocol-picklist-served-roundtrip.test.ts). Without it a GET → edit → PUT of any object with a picklist-bound field is refused with 422.list, create, edit the package-owned picklists⛔ No create, edit or delete: struck by ruling 6006062645, since the protocol takes no runtime create for this kind. A runtime-authored picklist would be a protocol change on its own card, never a consumer-side offer.the console pin bump that carries the spec/runtime halvesDone: objectuimainresolves@objectstack/spec17.6.0 since chore(deps): resolve @objectstack/* 17.6.0 in pnpm-lock.yaml and follow its mechanical contract moves; five owner-held reds reported by row (objectui#11438) #11531.Base at the next claim: objectui
main.Clause-②is the claim's to state; the expected answer isno(a consumer surface over the served field shape).Acceptance
A studio-authoring checklist item (authored here): a picklist authored in a package is referenced from two objects' select fields in the designer; both objects' forms show its options without a rebuild; the designer refuses inline options on the bound field; a GET → edit → PUT round trip of an object with a picklist-bound field succeeds.
pnpm objectui:refreshflows the build back to objectstack per the multi-repo rule.Dedupe words: picklist page, use picklist, object designer select field, global value set, studio.