Repository navigation
Commit 86dc686
feat(studio): switch a read-only package's flow off for this deployment from the Automations pillar (#28420)
<!-- claude-projects-attribution: {"github_login":"os-sales"} -->
_Requested by **Sales** · [project
thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4Uu85pY93xKTmJgLfTt1CGoVP)_
Fixes #28148
Clause-②: yes (widening)
Metadata-protocol: none
## 维护者速读
- **改了什么**:Studio 自动化里打开一个只读软件包(如
HotCRM)的流程时,头部的开关改为「本部署开关」:读写部署的启用账本(`_status` / `toggle`),文案为「本部署中已开启 /
本部署中已关闭 / 本部署中已开启,但未在此运行」,与 Setup ›
打包自动化同一套三种读法;可写软件包的流程仍是原来的定义开关和删除。Setup 页的开关请求挪进共用模块,两处发同一个请求。Clone 不在本
PR(归 #28149)。
- **为什么改**:以前只读流程头部的锁定开关读的是定义,旁边的状态读的是账本,会出现「状态: 已停用」旁边写「🔒
已启用」;管理员也无法就地关掉打包流程,只能去 Setup 找(ADR-0146 D4,裁决「28148 暂停解除」)。
- **风险与回滚**:只动前端;服务端仍是唯一裁判,在 group / isolated
姿态下非平台管理员看到禁用开关和一句原因,真拒绝时原样显示服务端的话。新增 `engine.studio.auto.ledger.*`
文案键(en、zh),属 Clause-② widening。回滚即 revert 本 PR。
-
**先看**:`ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx`(`ledgerRow`、`toggleLedger`、`flowSwitchEl`);`ui/packages/app-shell/src/views/studio-design/FlowLedgerSwitch.tsx`;`ui/packages/app-shell/src/views/setup/packagedFlowLedger.ts`。
- **两分钟验证**:showcase 里进 Studio → 只读软件包 → 自动化,打开
`showcase_urgent_task_alert`,点头部开关;预期文案在「本部署中已开启 / 已关闭」之间切换、网络面板只有一次
`POST
/api/v1/automation/showcase_urgent_task_alert/toggle`,旁边的状态随之一致;可写软件包的流程仍显示「已启用
/ 已停用」。
- **需要你做的**:无,席位落地。
## What changes
- **The ledger switch on the item** (`FlowLedgerSwitch.tsx`, new). On a
read-only package's open flow, when `GET /api/v1/automation/_status`
serves a row for it, the Automations pillar's header switch binds the
ADR-0126 §7.2 activation ledger instead of the definition:
`aria-checked` is the row's `enabled`, and a click sends `POST
/api/v1/automation/NAME/toggle` at once (no draft is written). Its word
is `activationReading` (`views/setup/packagedFlows.ts`) for the same
row, the reading Setup › Packaged automation shows: "On for this
deployment", "On for this deployment, not running here" (muted track,
the platform's `reason` as sent, described on the switch and in its
title, never parsed), "Off for this deployment". A writable package's
flow keeps its definition switch and its Delete, unchanged. Two
switches, two words.
- **Posture-aware, refusal verbatim.** `useFlowLedgerWalledReadOnly`
(`views/setup/packagedFlowLedger.ts`) pre-judges the toggle gate
(`activation-gate.ts#refuseUngrantedActivationWrite`) from the two
served facts, `features.tenancyPosture` via `useTenancyPosture()` and
`user.isPlatformAdmin`, the posture-and-rung half of
`walledCatalogReadOnly` without its catalog-type filter (director note
6526396053; ⛔ not that function, which answers `false` for a flow).
Under `group` / `isolated` a non-operator gets the switch disabled with
the console's own sentence; after any attempt the server's refusal is
shown verbatim in the pillar's refusal strip.
- **One toggle request.** `requestFlowToggle` moved out of
`PackagedAutomationPage.tsx` into `packagedFlowLedger.ts`; both surfaces
call it with their own fallback words. Setup's clone request stays on
Setup's page, untouched.
- **The rail keeps `unboundCause`.** `flowRailState` now keeps the
`_status` row's `unboundCause` (non-empty string only) beside `reason`,
so `activationReading` reads it for presence. The rail, the header
status and the Problems panel read exactly as before.
- **i18n.** A contiguous block of `engine.studio.auto.ledger.*` keys in
the studio namespace (en, zh). This is the widening behind `Clause-②:
yes (widening)`.
- **Resumed work.** The branch merges the unreviewed WIP
`claude/project-thread-dhj1ou@1a9e770` (release 6541889272) and then
drops its Clone half per ruling 6542841890: `FlowCloneDialog.tsx`, the
Clone menu entry, the shared clone request and the
`engine.studio.auto.clone.*` keys are gone; `FlowItemMoreMenu.tsx` and
the Delete test are byte-identical to `main`.
## Tests
`ui/packages/app-shell/src/views/studio-design/AutomationsPillar.ledgerSwitch-28148.test.tsx`,
8 cases, the metadata server doubled at the client and the `/automation`
doors doubled at `fetch`:
- the switch reads the ledger, not the definition: definition `obsolete`
+ ledger on reads on; a flip posts `{ enabled: false }`, re-reads
`_status`, saves no draft, toasts;
- the measured disagreement (definition `active`, ledger off) reads "Off
for this deployment", one switch only, no read-only hint;
- an enabled flow with an `unboundCause` reads "not running here" with
the platform's sentence, wired into `aria-describedby`;
- `group` posture, non-operator: disabled, the console's sentence,
nothing sent; CONTROLS `single` and `isolated` + operator: live;
- a 403 refusal is shown verbatim and the switch keeps the ledger's
reading;
- CONTROL: a writable package's flow keeps its definition switch and
Delete, and its switch never reaches the ledger door.
All readings below are on head `b328333c86` (this branch after its one
merge of `origin/main` at `3fb1a24540`), the dependency closure rebuilt
first (`turbo run build --filter='@object-ui/app-shell^...'`, 64/64).
<details><summary>Tests and type-check</summary>
- `pnpm --filter @object-ui/app-shell --filter @object-ui/i18n run
type-check` (both run `tsc --noEmit` and `tsc -p tsconfig.test.json`, so
the tests are compiled): lock VERDICT command-exit 0.
- `pnpm --dir ui exec vitest run --maxWorkers=2
packages/app-shell/src/views/studio-design/
packages/app-shell/src/views/setup/ packages/i18n/`: Test Files 386
passed (386), Tests 3573 passed, 13 skipped (3586).
- The new file alone, verbose: Tests 8 passed (8).
- eslint on the seven changed source files: 0 errors; the 22 warnings
are all on lines this diff does not touch.
</details>
<details><summary>Ablations (each through scripts/ablation-replace.mjs,
restored to blob == HEAD, git diff HEAD empty)</summary>
Run on the committed head before the main merge (`6d9de7534a`; the merge
touched none of these files). Each mutation landed (anchor 1 to 0) and
the ledger test file went red:
- A1 `checked={ledgerRow.enabled}` to `checked={flowEnabled}` (bind the
definition): 4 failed (ledger binding, measured disagreement, not
running, refusal).
- A2 `reading={activationReading(ledgerRow)}` to a two-way on/off: 1
failed (not running here).
- A3 `flowLedgerWalledReadOnly` returns `false`: 1 failed (walled
non-operator).
- A4 the toggle refusal uses the fallback instead of
`actionErrorDetail`: 1 failed (refusal verbatim).
- A5 the ledger row no longer requires `readOnly`: 1 failed (writable
package CONTROL). The first A5 attempt was a no-op the tool refused (its
replacement was a substring of its anchor, so the count did not rise);
re-run with a distinct replacement.
</details>
## Acceptance notes
- When `_status` serves no row for the open flow (an older backend, or a
flow the engine never registered), there is no ledger flag to bind, so
the read-only definition switch stays as it was, with its lock and the
read-only package hint. Card item 3 removes that hint where the ledger
switch is drawn, which is every case with a row.
- The PR body's attribution line links this card thread's project
thread, as the harness requires; the seat order names the seat thread
instead. Noted, not corrected.
- Branch name is the platform-assigned card-thread branch
`claude/project-thread-krgxy0`, the recorded exception to
`claude/issue-N-slug` (claim 6542901778).
## Verification
<details><summary>Derived gates: node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack, 34 commands, on
b328333</summary>
```
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
```
`--ran` reconciliation: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN (a
derived zero, every line carries its exit code). `node
scripts/check-sdui-manifest.mjs` is in the list (exit 0). The five
type-check-program CI steps the derivation names are CI's.
</details>
- Serial: no open PR touches `studio-design/**`, `views/setup/**` or the
studio i18n namespace (read from each open UI PR's file list against its
merge base). `git merge-tree --write-tree` of this head with
`origin/main` is clean.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L6bhgXdweWzW44zdh5YEjY)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L6bhgXdweWzW44zdh5YEjY
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 380a107 commit 86dc686
8 files changed
Lines changed: 622 additions & 48 deletions
File tree
- .changeset
- ui/packages
- app-shell/src/views
- setup
- studio-design
- i18n/src/namespaces/studio
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
98 | | - | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
99 | 101 | | |
100 | 102 | | |
101 | 103 | | |
| |||
156 | 158 | | |
157 | 159 | | |
158 | 160 | | |
| 161 | + | |
159 | 162 | | |
160 | 163 | | |
161 | 164 | | |
| |||
238 | 241 | | |
239 | 242 | | |
240 | 243 | | |
241 | | - | |
242 | | - | |
| 244 | + | |
243 | 245 | | |
244 | 246 | | |
245 | 247 | | |
| |||
364 | 366 | | |
365 | 367 | | |
366 | 368 | | |
367 | | - | |
368 | | - | |
369 | | - | |
370 | | - | |
371 | | - | |
372 | | - | |
373 | | - | |
374 | | - | |
375 | | - | |
376 | | - | |
377 | | - | |
378 | | - | |
379 | | - | |
380 | | - | |
381 | | - | |
382 | | - | |
383 | | - | |
384 | | - | |
385 | | - | |
386 | | - | |
387 | | - | |
388 | | - | |
389 | | - | |
390 | | - | |
391 | | - | |
392 | | - | |
393 | | - | |
394 | | - | |
395 | | - | |
396 | | - | |
397 | | - | |
398 | | - | |
399 | | - | |
400 | | - | |
401 | | - | |
402 | | - | |
403 | | - | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
404 | 379 | | |
405 | 380 | | |
406 | 381 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
0 commit comments