Repository navigation
Commit 4049efa
fix(app-shell): Studio Access refuses a new permission set over a taken identifier and can discard a set's draft (#26916)
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} -->
_Requested by **Steedos** · [project
thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbY2VZCDU1LZxstSD9mUKVAR)_
Fixes #26836
Clause-②: no
Metadata-protocol: none
## 维护者速读
-
**改了什么**:Studio「访问」页新建权限集前,先查已发布和草稿中的同名权限集(跨所有软件包);标识已被占用就拒绝、不写入,提示里点名那个权限集,属于本软件包时给出「打开它」。另外,打开的权限集若有待发布草稿,标题栏多一个「放弃草稿」。
- **为什么改**:原来的首写保护(`If-None-Match: *` +
草稿模式)只拦已有草稿,不拦已发布的同名权限集,于是新建会在线上权限集上叠一份空白草稿,发布软件包就会清空它的全部授权;Studio
里也没有办法放弃这份草稿。
- **风险与回滚**:低。只改 `AccessPillar` 一处的新建与放弃路径,沿用现有接口(列表、草稿列表、`DELETE
?state=draft&package=`);服务端、spec 不动。回滚即还原本 PR。
-
**先看**:`ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx`
的 `AccessPillar`(`doCreate` 新增的占用检查、`discardDraft`、对话框 `extra`
里的拒绝提示);`StudioDesignSurface.accessCreateTaken-26836.test.tsx`。
- **两分钟验证**:`pnpm --dir ui exec vitest run
packages/app-shell/src/views/studio-design/StudioDesignSurface.accessCreateTaken-26836.test.tsx`,预期
9 个测试全绿。
- **需要你做的**:无,席位落地。
## What changes
- **Create over a taken identifier is refused before anything is
written.** `AccessPillar`'s `doCreate` now reads the published
permission sets and the draft headers across every package
(`client.list('permission')`, `client.listDrafts({ type: 'permission'
})`, the same unscoped pair the Interfaces pillar's create reads). A
name either holds is refused in the dialog with a sentence naming the
set (its label and identifier). When the set is this package's (on the
rail), the dialog offers "Open “LABEL”", which closes the dialog and
opens it; a set another package holds gets no open path, and the
sentence says it is outside this package.
- **A fresh name is created as before**: the draft save with `mode:
'draft'`, `packageId`, `ifNoneMatch: '*'`, `surface: 'studio'`, the
toast, and the new set opened.
- **Discard draft.** An open set with a pending draft shows "Discard
draft" in its header (beside Explain access), hidden on a read-only
package. It confirms (one sentence for a published set, another for a
set never published), then calls `client.reset('permission', NAME, {
state: 'draft', packageId })`, the `DELETE ?state=draft&package=` door
the metadata editor's discard uses. A published set goes back to its
live body (the matrix re-reads it, as after a publish); a set never
published leaves the rail. A refused discard is a toast with the door's
message.
- Six new `studio` namespace rows in `@object-ui/i18n`, `en` and `zh`.
One root changeset: `@object-ui/app-shell` patch and `@object-ui/i18n`
patch.
- No `packages/spec/**`, no server change.
## Tests
`StudioDesignSurface.accessCreateTaken-26836.test.tsx`, nine cases on
the real `AccessPillar` and `PermissionMatrixEditPage`, over a client
double that keeps published and draft rows per package and refuses a
first-write pin over a draft row the way the door does:
- create over a published id of this package: refused, `save` never
called, no draft row, the sentence names the set, and "Open" opens it
(its header and grant count);
- create over a published id another package holds: refused, nothing
written, no "Open";
- create over a draft id: refused before the save;
- a draft that lands between the check and the save: the save still
carries `ifNoneMatch: '*'` and the door's 409 is shown, the other draft
untouched (objectui#11773);
- a fresh id: saved with the four options above, toast, rail row and
header;
- discard on a published set: `reset` called with `{ state: 'draft',
packageId }`, the matrix goes from 0 to 1 object granted (the live set),
the set stays on the rail, the control goes away;
- discard on a set never published: it leaves the rail and the remaining
set opens;
- a declined confirm discards nothing;
- no discard control without a pending draft, nor on a read-only
package.
## Acceptance notes
- **The door half is not a server defect, so there is no `domain:server`
child.** The metadata door does what the spec declares:
`SaveMetaItemRequestSchema.parentVersion` says a present `null` "asserts
'no current row of this lifecycle'" (on the REST door, `If-None-Match:
*`), and the `@objectstack/metadata-protocol` changelog entry that
opened the header says `If-None-Match: *` "with `?mode=draft` for a
draft … saves only where no row of that lifecycle exists". A draft-mode
pin over a published row is therefore admitted by contract (it is how a
client says "start a draft only if nobody has one"). Making the door
judge every lifecycle would be a semantic change to that declared pin,
beyond this card.
- **Race window of the client-side check:** a set published (or created
in another package) between the pillar's two reads and its save is not
refused by the pin; the save then writes a blank draft over it, as
before this change. A draft that lands in that window is still refused
by the door (tested above).
- The "Open" path exists only for a set on this package's rail; a set
another package holds cannot be opened from this pillar, so the refusal
there names it and asks for another identifier.
## Verification
All at head `4dd29301`, in a worktree off `origin/main` `c4a4aefc`.
<details><summary>New tests, ablation, type-check and nearby
suites</summary>
- `pnpm --dir ui exec vitest run
packages/app-shell/src/views/studio-design/StudioDesignSurface.accessCreateTaken-26836.test.tsx`
under `scripts/pm/os-verify-lock.sh`: `Test Files 1 passed (1)`, `Tests
9 passed (9)`.
- Ablation (`node scripts/ablation-replace.mjs`, anchor `if
(publishedRow || draftRow) {` replaced by `if (false && (publishedRow ||
draftRow)) {`, anchor x1 to x0): the three refusal cases go red (`Tests
3 failed | 6 passed (9)`); restored, blob equal to HEAD and `git diff
HEAD` empty. The tests import the pillar from source, so no build sits
between the mutation and the run.
- `pnpm --filter @object-ui/app-shell run type-check && pnpm --filter
@object-ui/i18n run type-check` under the lock: `VERDICT command-exit 0`
(app-shell's covers the test file).
- Narrowed suites under the lock: every app-shell test file that names
`AccessPillar`, the `studio` namespace or an `engine.studio.access` key
(66 files), plus all of `packages/i18n/`: `Test Files 155 passed (155)`,
`Tests 2159 passed | 13 skipped (2172)`. CI's Test UI runs the rest of
app-shell.
- `pnpm run check:i18n-designer-parity` (from `ui/`): "Every en row has
a zh row, and every shared row carries the same placeholders." `pnpm
check:new-line-citations` (from `ui/`): `0 new citation(s)`.
</details>
<details><summary>Derived dispatch gates (34/34)</summary>
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 34 commands; every one exited 0, the
PR-body gates (`check-closing-keyword-parity`,
`check-adr-0087-registration`, `check-changeset-no-major`) against this
body.
- `pnpm --dir ui exec vitest run scripts/` under the lock: `Test Files
178 passed | 2 skipped (180)`, `Tests 5065 passed | 2 skipped (5067)`.
- `pnpm check:type-check-debt` under the lock: `0 raw tsc error(s)
total, none above its recorded number`.
- `dispatch-gates --ran`: `34 derived famil(ies) accounted for — 34 run,
0 NOT-MEASURED`.
</details>
Dogfood battery: not run, no `!` in the title and no `(narrowing)` arm.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QsR2jRu5CpdGJFtUFzPvUy)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent c90b5b1 commit 4049efa
5 files changed
Lines changed: 491 additions & 6 deletions
File tree
- .changeset
- ui/packages
- app-shell/src/views/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 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 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 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
0 commit comments