Skip to content

Studio publish sheet lists an "Options" change for a picklist-bound field nobody edited, because it compares the stored draft with the live read that carries the resolved options #29899

Closed

Description

@objectstack-fleet

domain:devx#1 · os-warren · session_01JQwxbXGDmSYVyFomEEAdU3

Filing gate: ① user-visible UX defect, stable and with a named landing point (reach: the Studio publish review sheet, reproduced twice: a Studio add-field draft and an API draft that changed only another field's label).
Business reason: the publish sheet is where an author checks what they are about to ship. On any object that has a field bound to a package picklist, it reports a change to that field's options on every publish, although nothing changed. An author either distrusts the sheet or goes looking for an edit they never made, and a real options change on the same field would read the same.
Reader who acts: domain:ui, ui/packages/app-shell/src/preview/DraftChangesPanel.tsx (the diff the sheet draws).
Dedupe (open and closed): picklist publish sheet options changed 11 hits, DraftChangesPanel 30. Nearest: #27835 (closed, naming each change in words) and #29704 (closed, plain words on the sheet). None is about a phantom change.

QA-source: #29893 · studio-authoring.picklist-bound-select · observed beside the clauses

What happens

Measured at framework 589049b0 on a stock showcase boot with the Console built from the same tree, as admin.

  1. Declare a picklist in a package (this run added showcase_tier, Bronze / Silver / Gold, to a scratch copy of the showcase, as the item's fixtures.requires[0] asks). In a runtime package, publish an object with a field tier of {type: "select", picklist: "showcase_tier"} (stored without options, as a picklist-bound field must be).
  2. Save a draft of that object that changes only fields.notes.label (through Studio or PUT /api/v1/meta/object/OBJ?mode=draft&package=PKG).
  3. Open /_console/studio/PKG/data?surface=object:OBJ and click Publish.
  4. Expected: one line, ~ Notes … · Label.
  5. Actual: two lines, ~ Notes Again notes · Label and ~ Tier tier · Options.

Likely mechanism

The draft stores the bound field without options, while the live read serves it with the options the server resolved from the picklist. The sheet's diff compares the two and reads the resolved options as an edit. It should compare like with like (both stored, or both resolved), or ignore options on a field that declares picklist.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: first-touch audit · p2 · domain:ui · pm:queue · 2026-11-03T10:59Z

    ⛔ Not a claim.

    • Read fresh. Reproduced twice: Studio's publish sheet lists an "Options" change on every picklist-bound field nobody edited, because it compares the stored draft with the live read that carries the resolved options. The author's review surface lies, so p2.

    Generated by Claude Code

  2. added
    bugSomething isn't working
    domain:uiDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 3, 2026
  3. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-support-ai · session_01WGufzjDyeFwq6AuFu3vwTd

    Claim: PM loop round 1 · 2026-11-03T15:49Z
    Session: session_01WGufzfC4w4tZzdzVyV5FGX (card thread; dispatching seat session_01WGufzjDyeFwq6AuFu3vwTd)
    Account: os-support-ai
    Branch: claude/project-thread-xkxln8 (platform-assigned to the card thread; it can push only this branch)
    Worktree: objectstack-issue-29899
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: ui/packages/app-shell/src/preview/DraftChangesPanel.tsx and the diff builder it uses, their tests, one root .changeset/ file (stop on breach; explain in the report)
    Container & model: S/M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: ui code produces it (domain:ui) | no platform path covers it | every Studio/Console user who reaches the surface named in the card body
    Thread-read: 6601964711
    Premise: holds (re-read on origin/main 21510bba: the code the card names is unchanged)
    Serial constraints cleared: none (no other domain:ui claim touches ui/packages/app-shell/src/preview/**; #29897 is in views/metadata-admin/ResourceEditPage.tsx)
    Order: /mnt/project-files/ui-orders/29899.md · maintainer scope 「车队可以继续派发了,只派发 p1 p2 ,总并发不超过 20」 (2026-11-03T15:40Z)

  4. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-support-ai · session_01WGufzfC4w4tZzdzVyV5FGX
    os-dev-report
    Stamp: 2026-11-03T16:41Z

    {
      "issue": 29899,
      "status": "done",
      "branch": "claude/project-thread-xkxln8",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29949",
      "session": "session_01WGufzfC4w4tZzdzVyV5FGX",
      "premise_still_valid": true,
      "summary": "computeChangeDetail (the diff behind Studio's publish review sheet) now passes both envelope-free bodies of an object through dropServedPicklistOptions (@object-ui/data-objectstack, the writers' served-to-authored step), so a picklist-bound field is compared in its stored form on both sides. Chose 'compare like with like (both stored)' over 'skip options in the diff': it touches only definitions that name a picklist, so inline-options changes stay listed and a move between picklist and inline options still lists both keys. Mechanism confirmed in code: readMetaItem returns early for state=draft (stored row, no fold), while the active read folds through finishServedObject → resolvePicklistFieldsOnto. Serving resolved options is the declared PicklistServedFieldSchema contract, so the fix is ui-side, not a server fork. No path found where the draft itself carries resolved options.",
      "tests": "New DraftChangesPanel.picklistServedOptions-29899.test.tsx: 3 passed (bound field + label-only draft renders exactly one row '~ Again notes notes · Label'; CONTROL inline-options select with changed options renders '~ Status status · Options'; computeChangeDetail direct incl. bound/inline moves and array shape). Ablation at f4d64a21 via scripts/ablation-replace.mjs (anchor 1 → 0, blob 694ec6852677 → eb00fb8b831c; test imports src, no dist hop): 3 failed, e.g. 'expected [ ~ Again notes notes · Label, …(1) ] to have a length of 1 but got 2'; restored blob == HEAD, git diff HEAD empty. app-shell narrowed vitest over the 84 files naming DraftChangesPanel: Test Files 84 passed (84), Tests 536 passed (536), VERDICT command-exit 0. Full app-shell suite declared to CI (Test UI).",
      "gates": "At f4d64a21 under os-verify-lock: closure build turbo --filter=@object-ui/app-shell^... VERDICT command-exit 0 (64/64); pnpm --filter @object-ui/app-shell build VERDICT command-exit 0; pnpm --filter @object-ui/app-shell type-check VERDICT command-exit 0; node scripts/check-sdui-manifest.mjs exit 0. dispatch-gates --commands: 35 derived, all 35 exit 0; dispatch-gates --ran exit 0 (35 run, 0 NOT-MEASURED, 0 UNRUN). PR check-runs: in_progress at report time (not awaited).",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no MCP GitHub calls",
      "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, run 41676451797, PR #29949 read back identical, assignees none); this os-dev-report comment via post-stamped.mjs (POST /repos/objectstack-ai/objectstack/issues/29899/comments). git push not counted.",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-support-ai · session_01WGufzjDyeFwq6AuFu3vwTd

    ACCEPT → PR #29949 comment 6609921443 · head f4d64a21 · 2026-11-03T16:44Z. Landing waits for every check green on that head.

  6. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-support-ai · session_01WGufzjDyeFwq6AuFu3vwTd

    Landed · PR #29949 · 40fa433539 · 2026-11-03T17:30Z

    Probe Reading
    PR #29949 merged by the merge queue, merge commit 40fa433539
    origin/main content ui/packages/app-shell/src/preview/DraftChangesPanel.tsx:698 runs dropServedPicklistOptions on both bodies for object

    ACCEPT 6609921443 · card closed by Fixes #29899 (completed); pm:dispatched removed in this stroke.

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:uiDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions