Skip to content

feat(studio): external-object fields show their datasource and write state; no DDL controls on an external object - #27316

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-6ml89f
Oct 29, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-6ml89f

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by 田騏 · project thread
Fixes #27279

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么: Studio 对象设计器(字段画布与字段检查器)里,带 external 绑定的外部对象:每个字段标「来自 数据源名 · 只读或可写」,画布顶部一条说明;不再出现「添加字段」、批量删除,字段检查器里 API 名与类型置灰、无复制与删除。
  • 为什么改: 外部对象的表属于远端数据库,平台从不对它做 DDL;以前设计器照常提供加列、改类型、删字段,而平台不会对远端表执行这些改动(ADR-0015 欠的 Studio 半边,裁决 D6 的 P1 部分)。
  • 风险与回滚: 只影响带 external 的对象的设计器界面,平台自有对象不变;不改元数据协议、不改服务端。回滚即还原本 PR。
  • 先看: previews/external-object.tsx(判定与写入状态,含数据源读取)→ previews/ObjectFormCanvas.tsx(隐藏加字段与批量删除)→ inspectors/ObjectFieldInspector.tsx(ddlLocked 一行决定四个控件)。
  • 两分钟验证: pnpm --dir ui exec vitest run packages/app-shell/src/views/metadata-admin/previews/external-object.test.tsx,预期 11 passed;其中一条断言中文标记为「来自 plan_gantt · 只读」且无「添加字段」按钮。
  • 需要你做的: 无,席位落地。

What changes

An object that carries an ADR-0015 external binding is never a DDL target (the runtime's own test is the same presence check, isFederatedObject in @objectstack/objectql). The object designer under ui/packages/app-shell/src/views/metadata-admin now reads object.external / object.datasource and:

  • Marks every field. Each field row on the canvas and the field inspector carry a badge "From DATASOURCE · STATE" (zh: 「来自 DATASOURCE · 只读」), and a note above the canvas names the datasource and says why fields cannot be reshaped there.

  • Write state, never guessed. ADR-0015 writes need both datasource.external.allowWrites and object.external.writable (the engine's write gate, assertWriteAllowed). Without writable: true on the object the state is settled from the object alone: Read-only, and no request is made. With it, the datasource is read through the metadata door (GET /meta/datasource/NAME, the same item DatasourcePreview reads external.allowWrites from): both set ⇒ Writable, otherwise Read-only, and a datasource that cannot be read ⇒ "Writes unconfirmed" with a tooltip naming the missing half. The read is keyed on the datasource name, not on the client's identity (ui/AGENTS.md chore: version packages #10).

  • Hides the DDL affordances, each read from the code:

    Control Where Why it is DDL
    Add field (bottom bar, empty state, section header "+") ObjectFormCanvas addField appends a field = a new remote column
    Bulk Delete ObjectFormCanvas bulkDelete removes fields = drops remote columns
    API name input ObjectFieldInspector setKey renames the field key = renames the column
    Type picker ObjectFieldInspector changeType retypes the column
    Duplicate ObjectFieldInspector duplicateField adds a field
    Remove ObjectFieldInspector removeField drops the column

    The label-to-API-name derivation (deriveNameFor) is also off on an external object, since it renames the key. Kept: label, inline label rename, sections, ordering, bulk move-to-section, and the other display/validation settings, which change only platform metadata.

  • Strings: six designer.external.* rows in the studio namespace of @object-ui/i18n (en + zh), the folder's existing i18n mechanism.

Tests

previews/external-object.test.tsx (new, 11 cases): the binding reader; on the canvas, per-field and header badges "From plan_gantt · Read-only" with no datasource read, the zh mark 「来自 plan_gantt · 只读」 with no 「添加字段」, no Add field on an empty external object nor on a section header, bulk move kept and bulk delete dropped, the three writable: true outcomes (allowWrites ⇒ Writable, no allowWrites ⇒ Read-only, read failure ⇒ unconfirmed); in the field inspector, API name and Type disabled, Duplicate and Remove absent, Label still editable. Each group has a platform-owned-object control case that keeps every control.

Acceptance notes

  • Studio Data pillar is not covered by this PR. views/studio-design/StudioDesignSurface.tsx (outside this card's file surface, views/metadata-admin/**) has its own add-field doors: the records grid's "+" column (onAddColumn → AddFieldPopover) and the Form layout designer's onAddField. Its right rail reuses ObjectFieldInspector, so there the badge shows and rename / retype / duplicate / remove are already gone, but the grid and form-designer "Add field" still show for an external object. Named in the dev report for a follow-up.
  • The Ask AI button on the canvas's bottom bar stays; it opens the assistant and adds nothing by itself.
  • The datasource read happens only for writable: true objects; the canvas and the field inspector each read it once per datasource name.

Verification

All readings at head fdeadb17 on a shared container.

Affected tests and type-check
  • pnpm --dir ui exec vitest run packages/app-shell/src/views/metadata-admin/previews/external-object.test.tsx packages/app-shell/src/views/metadata-admin/previews/ObjectFormCanvas.test.tsx packages/app-shell/src/views/metadata-admin/inspectors/ packages/i18n/src/__tests__/studio-namespace.test.ts → lock VERDICT command-exit 0, Test Files 204 passed (204), Tests 2833 passed, 1 skipped.
  • pnpm --filter @object-ui/i18n --filter @object-ui/app-shell run type-check → VERDICT command-exit 0 (both tsc --noEmit and tsc -p tsconfig.test.json).
  • pnpm --dir ui exec vitest run scripts/ → VERDICT command-exit 0, Test Files 174 passed, 2 skipped; Tests 4990 passed, 2 skipped. (An earlier run of the same command exited 1 on a 15 s timeout of the whole-tree sweep in check-vi-mock-inherit.test.ts under shared load; that file alone passed 83/83, and the full re-run above is green.)
  • ui-local: check:new-line-citations (0 new citations), check:i18n-dead-keys, check:i18n-keys, check:i18n-drift (6 keys added, 0 en values changed), check:control-bytes → all exit 0.
Derived gate families (dispatch-gates --commands)

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran FILE → 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN; every command exit 0 (the list with exit codes is in the dev report). Also run outside the derivation: pnpm check:changeset-fixed exit 0. CI-owned type-check lanes and whole-tree families are left to CI.

🤖 Generated with Claude Code

https://claude.ai/code/session_017ihL2c5z3q4h8inrn5kwqV

…rite state; no DDL controls on an external object

An object with an ADR-0015 `external` binding is never a DDL target. The
object designer canvas and the field inspector now mark each field
"From <datasource> · <write state>" and stop offering add field, bulk
delete, rename, retype, duplicate and remove on such an object.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2c5z3q4h8inrn5kwqV
…aced beforeEach

An arrow returning the mock handed vitest a cleanup hook, which called the
rejecting mock after the test and failed it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2c5z3q4h8inrn5kwqV
The Records grid's "+" column and the Form toolbar's Add field are not
offered on an object with an ADR-0015 `external` binding, and the pillar
shows where its fields come from. Reorder and the column editor stay.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2c5z3q4h8inrn5kwqV
@github-actions github-actions Bot added size/l and removed size/m labels Oct 28, 2026
…ield inspector; changeset names the pillar

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2c5z3q4h8inrn5kwqV
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
ACCEPT · PR #27316 @ edaebc8a4cf2f7073ffe75d7d025f49c8fdc9d1c · 2026-10-28T17:34Z

维护者速读

  • 改了什么: Studio 里带 external 绑定的外部对象:对象设计器画布、字段检查器与 Data 支柱(记录表格、表单设计器)都标出「来自 数据源 · 只读/可写」,不再提供添加字段、批量删除、改 API 名、改类型、复制和删除字段。
  • 为什么改: 外部对象的表属于远端库,平台从不对它做 DDL;以前 Studio 照常提供这些按钮,点了也落不下去(裁决 D6 的 P1 半边)。
  • 风险与回滚: 只影响带 external 的对象的 Studio 界面;平台自有对象有对照用例保持原样。不改协议、不改服务端。还原本 PR 即回滚。
  • 先看: previews/external-object.tsx(判定与写入状态)→ StudioDesignSurface.tsx 的 DataPillar(canAddFields)→ ObjectFieldInspector.tsx(ddlLocked)。
  • 两分钟验证: 跑 previews/external-object.test.tsx 与 studio-design/DataPillar.externalObject-27279.test.tsx;中文断言「来自 plan_gantt · 只读」且无「添加字段」。
  • 需要你做的: 无,席位落地。

席位意见:ACCEPT。PR 正文仍是第一轮文字(head fdeadb17,含「Data 支柱未覆盖」一条):正文改写工具要求首行是席位签名,而项目要求归属两行在最前,两者不能同时满足,所以正文未更新;以本评论与卡上 os-dev-report 6476048591 为准。

席位核对

  1. Shape: diff read by the seat at edaebc8a (9 files, +608/−28): readExternalBinding is a presence test on draft.external, matching the runtime's isFederatedObject; write state needs both opt-ins and shows "unconfirmed" instead of guessing when the datasource read fails; canAddFields / ddlLocked gate every add/delete/rename/retype door named in the card and in REWORK round 1 (amendment 6474698138). Reorder, sections and labels stay editable (platform metadata only).
  2. Premise measured: the card's acceptance check is Studio-wide; round 1 left the Data pillar's grid "+" and form-designer Add field open, round 2 closes both (test DataPillar.externalObject-27279.test.tsx, with a platform-owned control case).
  3. Gate check-runs on edaebc8a: 55 runs — 44 success, 11 skipped (opt-in / not-applicable lanes), 0 failed or pending. mergeable_state: clean. Dev local: dispatch-gates --ran 34/34 run, 0 unrun.
  4. Accepted deviations: branch claude/project-thread-6ml89f (platform exception, in the claim); file surface widened by the seat's amendment; merge commit 97421253 carries git's default message; PR body not refreshed (signature-first vs attribution-first, above).
  5. Out of scope: none left open; the round-1 open question was folded into this PR. Clause-②: no · Metadata-protocol: none · Skills-docs: none — UI behaviour only, no authorable key.
  6. Landing: not governed (check-governed-merges --pr 27316: 0 of 9 paths, 636 lines ≤ 3000) → seat lands: pr_ready + automerge_enable through the relay.

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

Development

Successfully merging this pull request may close these issues.

ui(studio): external-object fields show their datasource and read-only state; no DDL affordances on an external object (epic #27261 P1, D6 first half)

2 participants