Skip to content

fix(studio): list an order-only field change in the publish review and pin platform fields on the form canvas - #27646

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-h5781r
Oct 29, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-h5781r

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

Fixes #27516

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:发布审阅单对「只调整了字段顺序」的对象草稿,展开后列出一行「字段顺序已变更」并保留 Update 标记,不再显示「No differences detected」;Studio 表单设计器里,与平台定义完全相同的字段(实际就是 Owner)在未分组区始终排在首位,把作者字段拖到它上方会落在它下方,拖放播报也报这个位置。
  • 为什么改:发布按字段顺序计算内容哈希、纯重排会真的发布新版本,但审阅单说没有差异;画布允许把字段放在 Owner 上方,刷新后平台又把 Owner 放回最前,作者的排列悄悄失效。
  • 风险与回滚:只动 Studio 两处界面与一个新文案键(10 个语言包各一行);不改元数据协议、不改服务端;回滚即 revert 本 PR。
  • 先看:DraftChangesPanel.tsx 的 fieldOrderChanged(排除哪些平台注入列);ObjectFormDesigner.tsx 的 settlePinned / dropLayout(onDragEnd 与播报共用一套落点计算);两份新测试。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/preview/__tests__/DraftChangesPanel.fieldOrder-27516.test.tsx packages/app-shell/src/views/studio-design/ObjectFormDesigner.pinnedFields-27516.test.tsx,预期 13 个用例全绿。
  • 需要你做的:无,席位落地。

What changes

  • Publish review sheet (DraftChangesPanel) — computeChangeDetail now reports fields.reordered for an object whose two versions declare their shared fields in a different order, and the drill-in prints a ~ Field order changed row (new key preview.changes.fieldOrderChanged). isEmptyChangeDetail counts it, so such an entry keeps its Update badge instead of the "No differences detected" glyph.
    • The order compared is the order the publish door stores. Left out on both sides: a column the platform injects while its definition is exactly the platform's (isInjectedColumnDefinition over injectedSystemColumnDefs, both from @objectstack/spec/data), which the /meta reads prepend and the draft write strips; and a column the platform injects and hides (isStudioHiddenSystemField, the search companion among them). Only fields both versions declare are compared; an added or removed field is already a row.
    • Gated on type === 'object', the one row of the door's ordered-map table; the comment on fieldOrderChanged names that table as the authority.
  • Form designer canvas (ObjectFormDesigner) — settlePinned places every shown ungrouped field whose definition, without its group, is the platform's own first in the ungrouped bucket, in the injection's order. It runs on the derived layout and on every drop. onDragEnd and the live-region dropPlaceIn now share one dropLayout (the arithmetic dropPlaceIn used to restate), so the place announced is the settled place committed. A pinned field moved into a section carries group and is the author's from then on; an edited one (a changed label) likewise. FieldControlPreview is untouched.
  • i18n — preview.changes.fieldOrderChanged in all ten locale packs.
  • Root changeset: @object-ui/app-shell and @object-ui/i18n patch.

Tests

  • New DraftChangesPanel.fieldOrder-27516.test.tsx (9 cases): a pure reorder of authored fields, the array shape of fields, a same-order control, platform columns placed differently on the two reads (registry-backed vs overlay-backed) compare equal, an author-declared owner_id counts, added/removed fields are not a reorder, non-object types are not compared; on the panel, the row prints and the Update mark stays, with a same-order control still reading "No differences detected".
  • New ObjectFormDesigner.pinnedFields-27516.test.tsx (4 cases), on the real DndContext with the band geometry of the objectui#11802 suite, and a reload that replays the round trip with the spec's helpers (strip what equals the platform's definition, prepend the injected columns): Condition dropped on Owner lands at 2 of 3, is announced there, and the reloaded canvas shows the same order; author reorders below Owner survive the reload; an author-changed owner_id keeps the place it is given; Owner moved into a section keeps its section order through the reload.
  • Ablation, one leg per behaviour, via scripts/ablation-replace.mjs (anchor 1 → 0, blob changed, restored with git diff HEAD empty); app-shell tests import the source, no build in the path:
Leg Mutation Result
review order reordered forced false 4 of 9 red (every positive case); controls green
canvas settle settlePinned returns the layout unsettled 1 of 4 red (the drop above Owner); the three author-order cases green, as they should be
  • pnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design/ packages/app-shell/src/preview/: 293 files, 2076 tests passed (includes the objectui#11802 announcement suite, whose drop-vs-commit control now runs through the shared dropLayout).
  • pnpm --dir ui exec vitest run packages/i18n/: 90 files, 1379 passed, 13 skipped (locale key parity included).
  • type-check for @object-ui/app-shell and @object-ui/i18n: exit 0. eslint on the four touched source/test files: 0 errors (8 warnings, all on lines this PR does not touch).

Acceptance notes

  • Triage reading, measured. Triage asked the review sheet to use "the same order-aware comparison publish already uses". That comparison is canonicalize(body, 'object') in @objectstack/metadata-core (its ORDERED_MAPS table keeps the order of an object's fields). It cannot be imported here as is: the module imports node:crypto at its top, and @object-ui/app-shell does not depend on @objectstack/metadata-core. The sheet therefore restates that one row's rule, keyed on the same type, and reuses the spec's exported injection helpers rather than a second list of platform columns. The seat approved this before it was written.
  • Where owner_id is re-injected, measured. The /meta read exits call governServedItem, whose applyInjectedSystemColumns (@objectstack/metadata-core) builds fields as the injected-but-undeclared columns followed by the declared ones; the draft write's stripInjectedSystemColumns removes a column whose definition equals the platform's (isInjectedColumnDefinition). The canvas pins exactly that set, read through the same spec helpers.
  • File surface. The ten ui/packages/i18n/src/locales/*.ts packs (one key each) were added to the claim's file surface with the seat's approval.
  • The canvas does not add a visible "pinned" marker: a field dropped above Owner lands below it and the live region names that place. A marker would need new designer copy; noted, not added.
  • A sighted drag still previews the card above Owner while the pointer is there (dnd-kit's sortable preview); the drop settles it. Noted.
  • Docs-driven rule (ui/AGENTS.md ✨ Set up Copilot instructions #2): no docs page describes the review sheet's field rows or the designer's ungrouped order; nothing to update.

Verification


Generated by Claude Code

…on the form canvas

The publish review sheet compared an object's fields by name only, so a
draft that only reorders them read "No differences detected" although the
publish door hashes field order and saves it. The drill-in now compares the
order of the fields both bodies declare, leaving out the columns the reads
inject and the writes strip, and lists a "Field order changed" row.

The form designer let an authored field sit above an ungrouped field whose
definition is the platform's own (owner_id). The draft write strips that
column and every read puts it back ahead of the declared fields, so the
arrangement reverted on reload. The canvas now settles such fields first in
the ungrouped bucket on every drop, and the drop announcement names the
settled place.

Claude-Session: https://claude.ai/code/session_01Utp6CZkEeDsL4LuR4hwqTN
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tooling labels Oct 29, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe

席位核对

ACCEPT · head a7a422a4fb77fc9e9f6972e0d6b1cabe7f67ec1e · 2026-10-29T16:21Z

  • Shape: draft → main, Fixes #27516 on its own line, no other card number beside a closing keyword. Clause-②: no and Metadata-protocol: none match the claim; @object-ui/app-shell is private, and @object-ui/i18n only gains the key preview.changes.fieldOrderChanged in all ten packs (seat-approved widening). No contract review owed.
  • Premise, measured: the publish review now lists Field order changed for an object whose draft only reorders fields (fieldOrderChanged: the fields both bodies declare, leaving out injected columns whose definition is the platform's own via the spec's injectedSystemColumnDefs / isInjectedColumnDefinition, and Studio-hidden system columns), so an order-only change no longer reads "No differences detected" while publishing saves it. The form designer pins platform-defined ungrouped fields (owner_id) first (settlePinned), and onDragEnd and the live-region announcement share one dropLayout, so the canvas shows the order a reload shows. Both seat-approved widenings (client order compare with the spec helper, 10 i18n packs) are within the claim.
  • Gates (check-runs on the head): 44 success, 11 skipped, 0 failure. check-governed-merges --pr 27646: NOT governed, 569 changed lines.
  • Accepted deviation: the attribution line links the card thread rather than the seat thread (same project, same requester).
  • Landing: non-governed, all checks green, mergeable_state clean ⇒ the seat lands it (pr_ready + automerge_enable).

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 29, 2026 16:23
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 29, 2026 16:23
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit 4278c3b Oct 29, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-h5781r branch October 29, 2026 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tooling

Projects

None yet

1 participant