Skip to content

feat(studio): customize a read-only base's flow into its pair, with one ledger switch and Restore package original - #28824

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

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #28570

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:在 Studio 的自动化栏位里,只读基础包(如 HotCRM)的流程头部多了「自定义」按钮。第一次保存编辑时,经 ensure 接口拿到配对包 BASE.custom,把这个流程以同名草稿写进去,并声明 overrides: { package, basedOn }。覆盖流程的头部显示「覆盖 HotCRM」徽标,仍然只用启用台账的那一个开关,不画定义开关。流程的 ⋯ 菜单多了「恢复软件包原版」,确认后删除配对包里的草稿和行,基础流程重新运行。
  • 为什么改:ADR-0146 D9 第 1–3 项([Epic] Package-level override: every customization of an installed package lives in its paired <base>.custom package, one declared override shared by code and Studio (supersedes #28130) #28236 的 N-18)。以前想改 HotCRM 的流程只能在 Setup 克隆,两份都会跑;现在用同名覆盖替换它,开关还是同一个。
  • 风险与回滚:低。只改 app-shell 的 studio-design 目录和 studio 文案(en/zh 各加一条),不改协议、服务端或 spec。是否已覆盖,读的是声明覆盖那一行本身(配对包草稿,否则 layered 读取的 overlay),不读信封里随基础包封印走的 deletable(服务端席位 1 的发现)。可写包的流程照旧有删除和定义开关(对照测试)。回滚即还原本 PR。
  • 先看:StudioDesignSurface.tsx 里 AutomationsPillar 的 doSave(写进配对包并声明覆盖)、restoreFlow(两次删除)和 flowSwitchEl(覆盖流程不画定义开关);然后 FlowItemMoreMenu.tsx 的 restore。
  • 两分钟验证:cd ui && pnpm exec vitest run packages/app-shell/src/views/studio-design/AutomationsPillar.customizeFlow-28570.test.tsx,预期 10 条全过。
  • 需要你做的:无,席位落地。

What changes

On a read-only base, the Automations pillar now customizes a flow the way the Interfaces pillar customizes a view, a dashboard or a report (the card's Wanted 1 to 6).

  • Customize this flow (Wanted 1). The flow's header draws StudioCustomizeButton, N-17's control, with the same block reasons: walled from the surface's customizeDoorWalled, noVersion when the listed manifest states no version. Pressing it only unlocks the flow's editors (canvas onPatch, the node inspector, the autosave). Nothing is written until an edit is saved.
  • The first saved edit (Wanted 2). doSave asks the surface's ensure door once per flow (StudioBaseCustomization.ensure, the same path InterfacesPillar takes) and writes the flow's draft into the package that answer names, under the flow's own name. The body is overrideDraftBody(draft, base, basedOn): the served flow without its _ keys, declaring overrides: { package, basedOn }. The first save into the pair is pinned to "no draft there" (ifNoneMatch), the later ones to the version they were answered. The flow's load reads its draft at the pair once one is listed, so the designer then edits that draft. The rest of the package stays read-only: Delete flow, New similar's target, the item tools and the definition switch keep their read-only gates.
  • One switch (Wanted 3). The open override keeps FlowLedgerSwitch on the flow's name (_status row, unchanged). When no ledger row is served, a customized flow draws no switch at all, where the base's own flow keeps its locked definition switch. toggleEnabled stays refused on a read-only package's flow, so Studio never writes the override's status to mean on or off.
  • The badge (Wanted 4). The header draws StudioOverrideChip from useServedOverride: the pair's draft through the draft door, else the layered read's overlay, which N-11 serves as the pair's row.
  • Restore package original (Wanted 5). FlowItemMoreMenu gains a restore prop that draws N-17's StudioRestoreOriginalItem. The pillar passes it when the served declaration exists. Per server seat 1's finding on the card, Restore is decided from the declared override's own row, never from the served envelope's deletable, which follows the base's seal. The confirmation (StudioRestoreOriginalDialog, new kind: 'flow') says the base's flow runs again under the same switch, and names the draft that goes first. Confirming deletes the pair's draft, then the pair's row, both at the pair (client.reset), reloads the flow, re-reads the declaration and the engine's rows, and the base's flow reads back.
  • Refusals (Wanted 6). A refused ensure door or a refused save into the pair is shown as the server sent it. A refused publish of the pair's flow goes through the surface's existing per-draft path, which carries the server's reason as sent (publishRefusal, engine.studio.refusal.publishPlain).
  • One new string, engine.studio.customize.restoreFlowBody, in the studio namespace's en and zh.

Tests

AutomationsPillar.customizeFlow-28570.test.tsx, 10 cases, through the mounted pillar, its real autosave, the real header controls and the real ensureCustomizationPackage over a fetch stub that also serves the /automation doors:

  • Customize is offered and writes nothing; until it is pressed the canvas edits nothing.
  • The first saved edit asks the door once (201) and writes the pair's draft under the flow's name with the declaration and no _ key; the base is untouched; no /toggle request is sent; the badge appears; one switch, the ledger's; the second edit asks the door no more and is pinned to v1.
  • A published override: badge, no Customize, the ledger switch alone, Restore offered although the layered envelope says deletable: false, no Delete flow, and an edit saves into the pair keeping the declaration.
  • Restore: the confirmation's title, the flow sentence and the draft sentence; two deletes at the pair (draft, then row); the base's flow reads back with Customize offered again.
  • No ledger row served: the base's flow keeps its locked definition switch (control); once customized no switch is drawn, and the saved body keeps the base's status.
  • A walled tenant admin: Customize disabled with the reason; nothing asked or sent.
  • A refused save into the pair is shown verbatim.
  • A refused publish of the pair's flow reads with the server's sentence.
  • CONTROLS: the base's flow with no override offers no Restore; a writable package's flow keeps Delete flow and its definition switch, with no Customize.

Acceptance notes

  • FlowItemMoreMenu lives in its own file, studio-design/FlowItemMoreMenu.tsx, not inside StudioDesignSurface.tsx, so the edit to it is that file. The claim names FlowItemMoreMenu in the file surface.
  • setup/packagedFlowLedger.ts was not touched: the ledger row is keyed by the flow's name, and N-11 keeps packagedFlowOwner(name) answering the base, so the existing hook already binds the override.
  • The flow's item tools stay read-only on a customized flow, as the Interfaces pillar keeps them on a customized item.

Verification

All local runs at head 146e0b60 unless stated.

Touched-package tests
  • New file: Test Files 1 passed (1), Tests 10 passed (10).
  • 155 ui test files naming AutomationsPillar, FlowItemMoreMenu, StudioCustomizeControls, the automations route, or importing the studio namespace, in four batches at 47ed4fb8: 40/40 files 336 tests, 40/40 files 851 tests, 40/40 files 283 tests, 35/35 files 587 tests, all passed. packages/i18n/: 94 files, 1438 passed, 13 skipped. The commits after 47ed4fb8 are a one-line render change to Restore's disabled (it no longer reads a ref while rendering), the new no-ledger-row case and two renamed test parameters. At 146e0b60 the new file and five neighbouring files (AutomationsPillar.ledgerSwitch-28148, .deleteFlow-27356, .newSimilar-28567, StudioDesignSurface.oneSurface-28572, InterfacesPillar.customize-28562) ran again: Test Files 6 passed (6), Tests 52 passed (52).
  • The rest of app-shell's suite is CI's Test UI (narrowing declared: the set above is a git grep of the touched components and strings, blind to tests that reach them indirectly).
Ablations (each one-shot through `scripts/ablation-replace.mjs`, restored to the HEAD blob, `git diff HEAD` empty)
  • Save names no pair: 2 red. Restore not read from the declaration: 2 red. Editors stay locked: 3 red. No badge: 2 red. Load ignores the pair: 1 red. Walled block dropped: 1 red. Definition switch drawn on a customized flow: 1 red (the no-ledger-row case). Dialog says the item sentence: 1 red. Row delete dropped: 1 red.
Type-check, lint, derived gates
  • pnpm exec turbo run type-check --filter=@object-ui/app-shell --filter=@object-ui/i18n --concurrency=2 at 146e0b60: Tasks: 66 successful, 66 total (the first run, at 92ba27eb, was red on two unused parameters in the new test's doubles, TS6133; fixed in 146e0b60).
  • ESLint on the six touched source and test files: 0 errors. In StudioDesignSurface.tsx the React Compiler's "memoization could not be preserved" warnings move from 27 to 29, for restoreFlow (the same shape deleteOpenFlow already warns on); no other warning class is added.
  • node scripts/pm/dispatch-gates.mjs --commands: 34 derived families, each run once at 92ba27eb and exit 0, including node scripts/check-sdui-manifest.mjs and pnpm --dir ui exec vitest run scripts/. --ran reconciliation: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. The only commit after it renames two test parameters.
  • Every family CI runs is read from this PR's check-runs.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01QsR2jRWZNXu1HAHuMowQXS

…tudio

On a read-only base, the Automations pillar offers Customize on a flow. The
first saved edit asks the ensure door and writes the flow's draft into the
pair under the same name, declaring the override. The ledger switch stays on
the flow's name, no definition switch is drawn for the override, the header
carries the override badge, and the flow menu offers Restore package
original, decided from the served declaration.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jRWZNXu1HAHuMowQXS
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 1, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

席位核对 · ACCEPT · 2026-11-01T22:06Z

Head-sha: 146e0b60969ce2b9f06c599ad7a520e507792862

  • Shape: draft → main, first content line Fixes #28570, no other card beside a closing keyword. 7 files, +737 / −23 (760 lines ≤ 3000), no governed path (check-governed-merges --pr 28824: 0 of 7). Clause-②: no / Metadata-protocol: none line-initial in body and changeset, as claimed (6563700673). Every file is inside the claimed surface (FlowItemMoreMenu lives in its own file, named by symbol in the claim).
  • Read in the diff (Wanted 1-6): on a read-only base's flow, Customize this flow reuses N-17's control and is disabled for the same walled principals; the first saved edit asks the ensure door once and writes the pair's draft under the same name with overrideDraftBody(...) declaring overrides: { package, basedOn }; the open override keeps the one ledger switch on the flow's name, draws no definition switch, and never writes its status for on/off; the header carries the declaration badge; Restore package original confirms, deletes the pair's draft then its row, and is offered from the served declaration of the override's own row, not from the envelope's deletable (server seat 1's finding 6559375158); a writable package's own flow keeps Delete flow and its definition switch; publish refusals are shown verbatim.
  • Gates (check-runs on this head): 44 success, 11 skipped, 0 failed or pending. mergeable_state clean.
  • Tests (dev): new AutomationsPillar.customizeFlow-28570 10/10; 155 neighbouring ui files and i18n 1438 tests green; 9 ablations each red then restored; type-check 66/66; derived gates 34/34 exit 0.
  • Out of scope / NOT MEASURED: no live Console run by the seat; the dev reported no out-of-scope finding. Skills-docs: not owed.
  • Landing: ordinary queue landing, pr_ready then automerge_enable. Fixes #28570 closes the card on merge. feat(setup): Setup's metadata pages open Studio's All metadata area; the catalog's Active switch moves to each item's page #28818 (Setup: retire Setup → Metadata behind redirects into Studio (datasource kept) and retire Packaged automation's flows half; Studio stops linking into Setup #28578 PR 2) also edits StudioDesignSurface.tsx; the queue rebuilds whichever lands later.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 1, 2026 22:07
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 1, 2026 22:07
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 1, 2026
Merged via the queue into main with commit 693e100 Nov 1, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-yoddf3 branch November 1, 2026 22:27
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 Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

2 participants