Repository navigation
fix(app-shell): Studio Access refuses a new permission set over a taken identifier and can discard a set's draft - #26916
Merged
Conversation
…en identifier and can discard a set's draft The create's first-write pin asserts that no draft row exists, so a name that derived to a published set's identifier wrote a blank draft over the live set. The create now reads the published and draft sets first and refuses a taken name, naming the set and offering to open it when it is this package's. An open set with a pending draft can now discard it. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jRu5CpdGJFtUFzPvUy
…r and the draft discard Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jRu5CpdGJFtUFzPvUy
This was referenced Oct 27, 2026
Conflicts: the studio namespace en.ts and zh.ts, where main appended the moved access, data, automation and datasource rows at the end of the table next to this branch's six rows. Kept both sides, main's rows first. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jRu5CpdGJFtUFzPvUy
Contributor
Author
席位核对ACCEPT · seat
Landing: no contract review is owed (no published schema, no governed text, |
This was referenced Oct 27, 2026
This was referenced Oct 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Steedos · project thread
Fixes #26836
Clause-②: no
Metadata-protocol: none
维护者速读
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
AccessPillar'sdoCreatenow 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.mode: 'draft',packageId,ifNoneMatch: '*',surface: 'studio', the toast, and the new set opened.client.reset('permission', NAME, { state: 'draft', packageId }), theDELETE ?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.studionamespace rows in@object-ui/i18n,enandzh. One root changeset:@object-ui/app-shellpatch and@object-ui/i18npatch.packages/spec/**, no server change.Tests
StudioDesignSurface.accessCreateTaken-26836.test.tsx, nine cases on the realAccessPillarandPermissionMatrixEditPage, 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:savenever called, no draft row, the sentence names the set, and "Open" opens it (its header and grant count);ifNoneMatch: '*'and the door's 409 is shown, the other draft untouched (objectui#11773);resetcalled 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;Acceptance notes
domain:serverchild. The metadata door does what the spec declares:SaveMetaItemRequestSchema.parentVersionsays a presentnull"asserts 'no current row of this lifecycle'" (on the REST door,If-None-Match: *), and the@objectstack/metadata-protocolchangelog entry that opened the header saysIf-None-Match: *"with?mode=draftfor 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.Verification
All at head
4dd29301, in a worktree offorigin/mainc4a4aefc.New tests, ablation, type-check and nearby suites
pnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design/StudioDesignSurface.accessCreateTaken-26836.test.tsxunderscripts/pm/os-verify-lock.sh:Test Files 1 passed (1),Tests 9 passed (9).node scripts/ablation-replace.mjs, anchorif (publishedRow || draftRow) {replaced byif (false && (publishedRow || draftRow)) {, anchor x1 to x0): the three refusal cases go red (Tests 3 failed | 6 passed (9)); restored, blob equal to HEAD andgit diff HEADempty. 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-checkunder the lock:VERDICT command-exit 0(app-shell's covers the test file).AccessPillar, thestudionamespace or anengine.studio.accesskey (66 files), plus all ofpackages/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(fromui/): "Every en row has a zh row, and every shared row carries the same placeholders."pnpm check:new-line-citations(fromui/):0 new citation(s).Derived dispatch gates (34/34)
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 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-debtunder 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.Dogfood battery: not run, no
!in the title and no(narrowing)arm.Generated by Claude Code