Skip to content

feat(app-shell): Studio adds an action to an installed app's object, saved in its customization package (#30088) - #30211

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-yom13c
Nov 5, 2026
Merged

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Yibo Wang · project thread

Fixes #30088

维护者速读

  • 改了什么:已安装(只读)应用的对象,在 Studio「操作」页现在可以「新建」操作:同一套预设和编辑器,保存为定制包(BASE.custom)里的独立 action 条目(带 objectName),列在厂商操作之后、带「新增」标记,可编辑、可删除,「仅看定制」会计入它。
  • 为什么改:运营想给已安装 CRM 的线索加一个「标记合格」按钮;以前只有可写包能加操作,最常见的已安装场景没有入口。
  • 风险与回滚:仅 @object-ui/app-shell 前端(外加两条 i18n 文案);无 spec、服务端、元数据类型改动;可写包的操作仍写在对象草稿里,行为不变。回滚即 revert 本 PR。删除已发布的新增操作是立即生效的(与删除流程一致),不经过发布。
  • 先看:pairObjectActions.ts(读写定制包里的操作,首写走 ensure 门)和 ObjectActionsPanel.tsx 里 pair 分支(厂商行只读、新增行可编辑)。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design/DataPillar.pairActions-30088.test.tsx(5 条全绿)。
  • 需要你做的:无,席位落地。

Clause-②: no
Metadata-protocol: none

What changes

On a read-only base package (installed or code-shipped) that has, or can get, its paired customization package (ADR-0146 D3), the Data pillar's Actions view of a base object now offers New. Card Acceptance 1–4 all ship here (diff about 940 changed lines, under the 1,500 split line).

  1. New on a read-only base. The Actions view gets an optional pair door from the Data pillar, passed only for a base object of a read-only base (extendsBase, the same condition the object-extension fields use). New offers the same presets (actionPresets.ts) and the same editor. A preset that needs no input is written at once; one that waits for a target is held in the panel as before and written by the first edit that gives it one. The write is a standalone action item into the pair, as a draft, objectName = the object, pinned ifNoneMatch: '*'. The first write asks the ensure door (customization.ensure) and every write names its answer, never a spelled .custom id. The object draft is not touched.
  2. Vendor rows read-only, added rows editable. The vendor's actions are the served object's actions less any the pair holds (by name, and by _packageId = the pair, which the served object carries on a folded pair action: measured below). They open read-only with no Delete. The pair's actions on this object (its published list and its drafts, each draft read at the address its save writes so the version pins the save) are listed after them with the compact "added" CustomizationMark. An edit autosaves into the pair's draft through the shared useDraftAutoSave and useDraftSaveGuard. Delete removes the pair's draft and then its published row, as Delete flow does. An added action keeps the name it was created with: a rename is held with a line saying how to save (same rule and reason as the hooks panel). The read's top-level _ keys are never written back.
  3. Pending changes and "only customizations". The draft is in the pair, so it appears in the surface's pending changes and publishes with the pair's batch (no change needed there). The Actions list gets the "only customizations (n)" toggle (RailCustomizationFilter), counting the added actions that carry a mark and narrowing the list to them.
  4. Walled caller. When the ensure door refuses this caller, New is not offered and the pair's actions open read-only.

The object rail's own "only customizations" count is unchanged: it counts rows by the base's served customization read, which lists overrides and object-extension members and no standalone action. Counting a base object there for a pair action would need either a change to that read (server and spec) or a client-side computation the mark rules forbid, so the count lives on the Actions list instead. See Acceptance notes.

New i18n key engine.studio.actions.renameHeld (en, zh).

Tests

DataPillar.pairActions-30088.test.tsx, real DataPillar, real Actions view and real action editor over a metadata client double, 5 tests:

  • New on a read-only base offers the four presets; change_choice writes one action save at the pair with objectName, mode: 'draft', ifNoneMatch: '*', asks the ensure door once, and writes no object; the vendor row has no Delete and a disabled editor; the added row carries the added mark.
  • A pair action (published and drafted) is listed once, as the pair's; a label edit autosaves into its draft at the pair with no _packageId; Delete resets its draft and then its row at the pair.
  • "only customizations" reads 1 and narrows the list to the pair's action.
  • Walled: no New, the pair's action read-only, no ensure call.
  • CONTROL: on a writable package the new action is written into the object draft's actions, with no action save and no ensure call.
Ablations (each through scripts/ablation-replace.mjs, restored to the HEAD blob, git diff HEAD empty)
  • Drop the pair route (extendsBase && customization to false && customization in the Data pillar's Actions branch): 3 failed / 2 passed (walled and CONTROL stay green).
  • Drop the door gate (canAdd = pair ? !pair.walled to pair ? true): 1 failed (walled) / 4 passed.
  • Write the read's _ keys back (ownBody keeps every key): 1 failed (the edit pin) / 4 passed.

Acceptance notes

Browser check on a dev boot of examples/app-crm (pnpm dev:crm -- --fresh) with the console on Vite, on f8bdead2 (before the merge of main). com.example.crm 4.0.0 is read-only and had no pair at boot. Screenshots are in the project's ui-orders/30088-shots/ folder.

  • Before (main's two files served, confirmed by fetching the served module): the 操作 tab lists the vendor's Convert Lead with no 新建 (01-before-no-new.png).
  • Add. 新建 shows the presets (02-after-new-menu-presets.png). 修改下拉选择字段 sent POST /packages/com.example.crm/ensure-customization (201, pair created) and PUT /meta/action/crm_lead_action_2?mode=draft&package=com.example.crm.custom (200). The row shows the 新增 mark, 仅看定制 (1), 删除, and the top bar 变更 1 (03-after-added-in-pair.png). Label 标记合格 and placement 列表 · 行 autosaved into the pair's draft (04-after-edited-saved-to-pair.png).
  • Publish. 待发布的变更 lists 动作 标记合格 新增 under "保存到 CRM (minimal example) 的定制" (05-after-pending-changes.png); 全部发布 sent POST /packages/com.example.crm.custom/publish-drafts, publishedCount: 1. Back in Studio the action is listed once, marked (06-after-published-listed-once-marked.png). GET /meta/object/crm_lead/layers?package=com.example.crm served it in effective.actions with _packageId: com.example.crm.custom, beside the vendor's with none.
  • Run. The runtime row menu of a lead shows 标记合格 (07-after-runtime-row-menu.png); it asked for 状态 (08-after-run-dialog.png), and with 合格 sent POST /actions/crm_lead/crm_lead_action_2 (200, operation: update); the row turned 合格 with "操作已成功完成" (09-after-run-result.png).
  • Remove. 删除 sent DELETE /meta/action/crm_lead_action_2?package=com.example.crm.custom (200); the list shows only Convert Lead (10-after-removed-in-studio.png) and the runtime row menu no longer has it (11-after-removed-runtime-menu.png).
  • A rename of an added action is held with the line, and no save is sent until the name is put back (12-after-rename-held.png); a second run confirmed the edit's PUT carries no _packageId and that 删除 of a draft-only action sends DELETE ...?state=draft&package=com.example.crm.custom.

Observed, not changed:

  • The object rail's 仅看定制 does not count crm_lead for an added action (see What changes). If the maintainer wants the object row marked too, the base's customization read would have to list standalone pair actions on base objects: a spec and server change, out of this card (Acceptance 5).
  • Deleting a published added action takes effect at once, as Delete flow does; it does not wait for a publish.

Verification

All under scripts/pm/os-verify-lock.sh unless noted. Head 238e15e3 (main 474c030b merged).

  • Build: full pnpm build, then turbo run build --filter=@object-ui/app-shell... after the merge: exit 0.
  • @object-ui/app-shell and @object-ui/i18n type-check (both tsconfigs each): exit 0.
  • Tests: every app-shell test file that imports DataPillar, ObjectActionsPanel or pairObjectActions (79 files) plus the whole @object-ui/i18n suite (95 files): 174 files, 1977 passed, 13 skipped. Narrowed from the 214 files importing StudioDesignSurface: this diff changes only the Data pillar's Actions branch there; CI's Test UI runs the rest.
  • npx eslint on the 4 touched source/test files (unlocked): 0 errors; the warnings are all pre-existing ones in StudioDesignSurface.tsx.
  • node scripts/pm/dispatch-gates.mjs --commands on head 238e15e3: 35 commands, all recorded :: exit 0 (including check-sdui-manifest.mjs, vitest run scripts/ and check:type-check-debt); --ran: "35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN".
  • Changed lines against main: about 940 (+908 / -30, 7 files), under 3,000.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WGufzemmgi8p8uY913FXHx

…stomization package

On a read-only base with a pair, the Data pillar Actions view offers New
with the same presets and editor, and saves the action as a standalone
action item in the pair (objectName bound), listed after the vendor
actions with the customization mark, editable and removable.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzemmgi8p8uY913FXHx
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tooling labels Nov 5, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · os-support-ai · session_01WGufzinedUGSXteN45poyo

维护者速读

  • 改了什么:已安装(只读)应用的对象,在 Studio「操作」页可以「新建」操作,存进定制包(BASE.custom)作为独立 action 草稿;厂商操作仍只读,新增的带「新增」标记,可编辑、可删除。
  • 为什么改:给已安装 CRM 的线索加「标记合格」这类按钮,是最常见的场景,以前没有入口(Studio: add a new action to an installed app's object, saved in the customization package #30088)。
  • 风险与回滚:只动 Studio 前端与两条文案;无 spec、服务端、元数据类型改动;可写包仍写对象草稿,行为不变。删除已发布的新增操作立即生效(与删除流程一致)。回滚即 revert。
  • 先看:pairObjectActions.ts 与 ObjectActionsPanel.tsx 的 pair 分支。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design/DataPillar.pairActions-30088.test.tsx。
  • 需要你做的:无,席位在 CI 全绿后落地。

席位意见:ACCEPT。四条验收都在一个 PR 里,范围守住;对象侧栏「仅看定制」不计入定制包操作,留作已知限制(见下)。

席位核对

ACCEPT → PR #30211 @ 238e15e392dea312977b06e0880eac4ef105d261 (card #30088, dispatch claim 6646589118).

  • Shape: draft, base main, first body line after attribution Fixes #30088 (merge should close the card: Acceptance 1–4 all ship). 7 files, +908/−30 = 938 changed lines (check-governed-merges --pr 30211: NOT governed, under 3,000).
  • Premise, measured: seat read on origin/main 07595c074 before dispatch (StudioDesignSurface.tsx actions branch passed disabled={readOnly}, no New). Diff read by the seat: the only StudioDesignSurface.tsx hunk passes an optional pair door to ObjectActionsPanel under extendsBase && customization; pairObjectActions.ts lists the pair's action items (published + drafts) filtered by objectName, writes client.save('action', name, body, { mode: 'draft', packageId: <ensure answer>, ifNoneMatch: '*' }), strips top-level _ keys before write-back. Browser shots read by the seat: 30088-shots/06 shows the read-only CRM 线索 Actions list with vendor Convert Lead and the added 标记合格 (SCRIPT, added mark, 仅看定制 (1)).
  • Gates: dev local (report 6647669576): 5/5 new tests incl. CONTROL (writable package still writes draft.actions), 3 ablations red then restored, 174-file sweep green, type-check green, dispatch-gates --ran 35/35 exit 0, 0 NOT-MEASURED. CI check-runs on this head: in progress at this record (8 success, 11 skipped, 14 running, 0 red); landing waits for every check green or expected-skip.
  • Accepted deviations: (1) the diff adds one key engine.studio.actions.renameHeld to ui/packages/i18n/src/namespaces/studio/{en,zh}.ts, outside the claim's declared surface: in scope (the new rename-held line's text), and the changeset names @object-ui/i18n (published) as patch — accepted. (2) Browser check ran on f8bdead2 before the main merge 474c030b, which touches no file of this diff — accepted.
  • Out of scope: the object rail's 仅看定制 count does not include pair actions because the served customization read lists no standalone pair action; counting it needs a spec + server change, outside Acceptance 5. Acceptance notes, not filed (no pull yet); card only if the maintainer wants the object row marked. Skills-docs: not owed (Metadata-protocol: none).
  • Landing: Tier S path (not governed): when all checks are green on this head, relay pr_ready + automerge_enable; on MERGED, landing note on Studio: add a new action to an installed app's object, saved in the customization package #30088 and remove pm:dispatched.

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.

Studio: add a new action to an installed app's object, saved in the customization package

1 participant