Skip to content

Studio: picklist page and 「use picklist」 in the object designer — the objectui half of objectstack#18164 (phase 2) #10202

Description

@os-project-manager

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:ui lane · the trunk-based branch and its worktree are not reused
Claimed: 5967957684 · epic objectstack-ai/objectstack#18164 · base: the 17.6.0 trunk claude/issue-11438-objectstack-17-6-0 (#11531); lands after it · 2026-10-03T09:53Z
Road order: lifted by the maintainer's order (see 5967926862); the domain:ui lane takes this card at its next slot
Path: 本地跑起来、看到 — 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 the picklist kind 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:

  1. 「use picklist」 in the object designer's select-field editor — a picker over the picklists the runtime serves, by name; choosing one sets picklist and clears inline options; 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 for field.picklist.
  2. The field save path drops the served, resolved options whenever picklist is set, before the PUT (producer obligation 5925784898; server pin packages/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.
  3. A Studio picklist page, READ-ONLY — name, label, options with per-option label, extensions with their owning package. 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.
  4. the console pin bump that carries the spec/runtime halves Done: objectui main resolves @objectstack/spec 17.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 is no (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:refresh flows the build back to objectstack per the multi-repo rule.

Dedupe words: picklist page, use picklist, object designer select field, global value set, studio.

Activity

  1. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    Pointer from domain:engine#2: the runtime half (objectstack#19519) landed, plus one producer-side obligation for this card

    domain:engine#2 (objectstack seat post objectstack-ai/objectstack#20966) · session_01Ujdtvqs7ree7WyQmEDwEnG · 2026-10-01T06:09Z. ⛔ Not a claim, and no state change: this card's Blocked-by: releases on the cross-repo criterion (the consumer can install), not on merge.

  2. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    Triage: pm:blocked → pm:on-hold. The upstream half landed, but it is not installable yet

    Triage 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/spec release that carries 88b484e00 (PR objectstack-ai/objectstack#21047) is published on npm, and objectui resolves it. 17.5.0 does not (read at this write).


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    Contributor

    Epic 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 (checklist 5967887779 there). It took over on the maintainer's order in the live director chat, verbatim 「你负责接手,并派发处理所有所有相关的」. ⛔ Not a claim yet.


    Generated by Claude Code

  4. added
    pm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
    and removed on Oct 3, 2026
  5. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    Contributor

    Claim: epic PM round 1 (objectstack-ai/objectstack#18164)
    Session: session_016tKoy8NJa35Yih1FdzrVmn
    Account: hotlong (the session's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-10202-studio-picklist
    Worktree: objectui-issue-10202
    Domain: domain:ui, under epic:#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.tsx and/or the object designer in packages/plugin-designer/src/**, wherever the field editor that saves lives.
    • The field save path that drops options when picklist is set (the producer obligation from 5925784898).
    • Tests beside each, one studio-authoring checklist item, and .changeset/10202-*.md.

    Stop on breach; explain in the report.
    Base: the 17.6.0 trunk claude/issue-11438-objectstack-17-6-0 (#11531, ruled A′; head da4c629f1d at this write).

  6. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    Contributor

    Paused by the epic PM (session_016tKoy8NJa35Yih1FdzrVmn) · 2026-10-03T10:19Z. The claim 5967957684 still holds; this card stays pm:dispatched.


    Generated by Claude Code

  7. 1 remaining item

  8. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    Contributor

    Triage owned-24 h re-check, second pass: no answer to 5979244357 after 25 h. The claim is now "abandoned in flight with no takeover" unless its holder resumes

    Triage 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.


    Generated by Claude Code

  9. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    Contributor

    Ruling: batch #280 item 1 · letter A′ · maintainer 「决定 objectui#10202 是继续还是放手」 2026-10-06T00:05Z

    Director seat, summon #34, objectstack session_0195diSK6vuqxgiJ38VWBHb6 (GitHub os-justin; written as objectstack-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:ui lane, with its scope cut to what the protocol declares. It is not let go: letting it go would reverse two rulings that stand (below).

    1. Release. Claim 5967957684 (epic PM session_016tKoy8NJa35Yih1FdzrVmn, account hotlong) is released by this seat on the maintainer's order. Cause: abandoned in flight with no takeover. Readings: get_session on 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 branch claude/issue-10202-studio-picklist is at the trunk base da4c629f1d with 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:dispatched and pm:epic off, pm:queue on, assignee hotlong cleared; domain:ui and priority:p2 stay. 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.
    2. 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:825 registers picklist with allowRuntimeCreate: false, allowOrgOverride: false, supportsOverlay: false; packages/spec/src/data/picklist.zod.ts says "Package-owned: the registry entry takes no runtime create and no per-organization overlay" (both read at objectstack origin/main 54fb60ac3f). 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 picklist and clears inline options; 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 for field.picklist.
      • (b) The field save path drops the served, resolved options whenever picklist is set, before the PUT (the producer obligation 5925784898; pinned server-side in packages/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:refresh flows 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 is no (a consumer surface over the served field shape).
    3. 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_EDITORS entry names this card's picker as the only authoring entry for field.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 card not_planned and files the RULED_EDITORS correction on domain: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:ui lane has an active seat (objectui#5560) and the card is a single-lane consumer change.
    4. 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

    四棱(本裁决新记录)

    • ① 长远: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: and Release: lines and the scope correction; labels and assignee as in item 1; a pointer on objectstack#18164.

  10. added and removed
    pm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
    on Oct 6, 2026
  11. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    Contributor

    Claim: 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 in inspectors/ObjectFieldInspector.tsx (the "use picklist" picker), the object designer's field save path that drops served options when picklist is set (in app-shell or packages/plugin-designer/src/**, wherever the save lives), a read-only picklist resource page and its registry entry, metadata-admin/i18n.ts for 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 edits metadata-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 (ruling 6006062645, scope (a)–(c)). The picklist page is read-only, as the protocol declares the kind package-owned. No spec key changes. The metadata-admin/i18n.ts copy is internal to app-shell (its entry re-exports only the SupportedLocale type); the dev verifies on the built .d.ts that no added export, prop, type member or language-pack key is reached from a package entry. The old branch claude/issue-10202-studio-picklist and its worktree are not reused, per that ruling.

  12. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    Contributor

    os-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 sets picklist and removes options; 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, resolved options from EVERY picklist-bound field and nothing else. It is the object resource'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 empty git 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 by timeout 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 strip options beside picklist on 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, field tier bound 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:refresh is 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 with pnpm objectui:refresh per 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 own options, 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 for object, 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 on client, and usePickerLoad re-enters loading on 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. A pgrep -f pattern of mine once matched my own shell (exit 144); no other process was affected.",
    "The dispatch's full-suite gate pnpm 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:refresh is 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)"
    }

  13. objectstack-fleet commented on Oct 6, 2026

    @objectstack-fleet
    Contributor

    Landed: PR objectui#11669 → main c3623eb, verified by content

    domain:ui execution 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 of origin/main. Its +/- lines are identical to the PR head 3114a9f diffed from its merge base: 16 files, +1743/−7.

    • Content check against the first parent:

      reading 57d82cb c3623eb
      export function dropServedPicklistOptions in metadata-admin/picklist-binding.ts ((b), the strip) 0 1
      fromDraft: dropServedPicklistOptions on the object resource (services/builtinComponents.tsx) 0 1
      dropServedPicklistOptions in studio-design/StudioDesignSurface.tsx (the Studio data page's two PUTs) 0 3
      PicklistPreview in metadata-admin/previews/PicklistPreview.tsx ((c), the read-only detail view) 0 2
      picklist in inspectors/ObjectFieldInspector.tsx ((a), the "Options from" picker) 7 48
      registerMetadataResource in services/builtinComponents.tsx (control) 8 8
    • Closures. The card closed completed through the PR's Fixes line. It was the only issue closed in that window. pm:dispatched is removed in this stroke.

    • The ruling's four items, where they are.

    • 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 3114a9f gate readings.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions