Repository navigation
fix(app-shell): publish review sheet lists no Options change on a picklist-bound field nobody edited - #29949
Merged
Conversation
…stored form The live read serves a picklist-bound field with the options the runtime resolved from its picklist; the strict draft read serves the stored draft, which never carries them. The sheet diffed the two as read and listed an Options change on every publish. Both sides of an object now go through dropServedPicklistOptions before the diff, so inline options keep every real change. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzfC4w4tZzdzVyV5FGX
Contributor
Author
|
domain:ui#1 · os-support-ai · session_01WGufzjDyeFwq6AuFu3vwTd 维护者速读(席位终稿)
席位核对 · ACCEPT · head
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Yibo Wang · project thread
Fixes #29899
维护者速读
options)来比,不再把服务端解析出来的选项当成作者改动。@object-ui/app-shell的面板 diff,不改任何写入、协议或存储;只影响声明了picklist的字段的options比较。回滚即 revert 本 PR。ui/packages/app-shell/src/preview/DraftChangesPanel.tsx的computeChangeDetail开头的stored(...)两行;新测试DraftChangesPanel.picklistServedOptions-29899.test.tsx。pnpm --dir ui exec vitest run packages/app-shell/src/preview/__tests__/DraftChangesPanel.picklistServedOptions-29899.test.tsx,预期 3 passed。Clause-②: no
Metadata-protocol: none
What changes
computeChangeDetail(the diff behind Studio's publish review sheet) now reads both versions of anobjectin the form the publish door stores a picklist-bound field: withoutoptions. It does so by passing each envelope-free body throughdropServedPicklistOptionsfrom@object-ui/data-objectstack, the function the Studio and Setup writers already use to turn a served object read back into the authored form before a PUT (objectui#10202, objectui#11692). The step runs only when the entry's type isobject.The panel already imports from
@object-ui/data-objectstack(readDraftChanges), so the console's eager graph gains no new module.Why "compare like with like (both stored)" and not "skip
optionsin the diff"The card offered both. Normalizing to the stored form keeps every real change visible; skipping a key would hide some.
dropServedPicklistOptionstouches a field only when that definition names a picklist (picklist !== undefined, theFieldSchemarefinement's own test). A field with inlineoptionsis returned by reference, so a real options change there is still listed. The CONTROL test proves it.picklistandoptions, in either direction, because the side with inline options keeps them. A diff-level "ignoreoptionswhen the field declarespicklist" rule would hide theoptionshalf of that move. It would also have to choose which side'spicklistcounts.picklistandoptionstogether, so the stored form of a bound field never carriesoptions. Normalizing to it loses nothing an author could have written.Mechanism, measured against
origin/mainat21510bbaThe card's PM assumptions hold:
readMetaItem(packages/metadata-protocol/src/protocol-meta-item-reads.ts) returns early forstate: 'draft'("Draft reads stop here"). It serves the converted draft row throughgovernServedObjectand never reaches the object fold. The active read folds the object throughfoldObjectExtendersFromRegistry→SchemaRegistry.foldObjectExtendersOnto→finishServedObject→resolvePicklistFieldsOnto(packages/objectql/src/picklist-resolution.ts). That last step writes the resolvedoptionsonto each bound field and keepspicklist.dropServedPicklistOptions), and the authoring door refuses a body that carries both keys. A body that did carry both would also be normalized here on both sides.PicklistServedFieldSchema,@objectstack/specdata/picklist.zod.ts): every object read serves it, and the write door judges values by the same served field. The draft read serving the stored row is ADR-0005's draft lifecycle. Neither read is wrong. The diff was comparing two forms, so the ui lane owns the fix.Tests
New file
ui/packages/app-shell/src/preview/__tests__/DraftChangesPanel.picklistServedOptions-29899.test.tsx. It renders the realDraftChangesPanelagainst routes where the published read servestierwith resolved options and the draft read serves it stored:tier, and a draft that changes onlynotes.label: the drill-in renders exactly one row,~ Again notes notes · Label, and noOptions.statuswhose options really changed, beside the same boundtier: exactly one row,~ Status status · Options.computeChangeDetaildirect: a bound field reads unchanged; bound → inline and inline → bound each list bothpicklistandoptions; the arrayfieldsshape reads the same.Ablation (fix removed → new tests red → restored)
Fix committed first (
f4d64a21). Thennode scripts/ablation-replace.mjsswapped the anchortype === 'object' ? dropServedPicklistOptions(body) : body;forbody; // ABLATION-29899and ran the new file under the verify lock. The test imports the panel by relative path fromsrc, so nodistrebuild sits between the mutation and the run.Without the fix, case 1 renders a second row beside the label row, and case 3 lists
tieras changed. The CONTROL fails only on its row count: its ownOptionsrow is still there first, and one more row follows it.Acceptance notes
typegate: onlyobjectbodies are normalized. Its one caller passesentry.type. The served picklist form is a property of object reads.Verification
All runs at head
f4d64a21, from theui/root asui/AGENTS.mdprescribes, underscripts/pm/os-verify-lock.sh.Local runs
turbo run build --filter='@object-ui/app-shell^...' --concurrency=2(closure)VERDICT command-exit 0, 64/64 taskspnpm --filter @object-ui/app-shell buildVERDICT command-exit 0pnpm --filter @object-ui/app-shell type-checkVERDICT command-exit 0pnpm --dir ui exec vitest run --maxWorkers=2over the 84 test files that nameDraftChangesPanelVERDICT command-exit 0, Test Files 84 passed (84), Tests 536 passed (536)node scripts/check-sdui-manifest.mjsNarrowed: the full
@object-ui/app-shellvitest suite is not run locally. It needs more than an hour on the shared box. The 84 files are every test that names the edited component. CI's Test UI runs the rest.Derived gates (`dispatch-gates.mjs --commands`)
35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN (
dispatch-gates --ranexit 0) atf4d64a21. Each line isCMD :: exit N.🤖 Generated with Claude Code
https://claude.ai/code/session_01WGufzfC4w4tZzdzVyV5FGX