Repository navigation
fix(react,components,app-shell): a delegated admin's out-of-scope save shows the server's reason - #26363
Merged
Conversation
…e shows the server's reason A 403 DELEGATION_SCOPE_VIOLATION carries the boundary of the caller's own delegated scope that refused the write. The record form and the flow-run path classed every 403 as a plain denial and showed the fixed form.noPermissionToSave line. delegationScopeMessage reads the code before isPermissionError; every other denial keeps the fixed line (ruling B' on the record-save rule, unchanged). Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JZRTRVa5FXXjuAJC6ZPM8P
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JZRTRVa5FXXjuAJC6ZPM8P
…-47phpr Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JZRTRVa5FXXjuAJC6ZPM8P
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JZRTRVa5FXXjuAJC6ZPM8P
…ause-② widening Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JZRTRVa5FXXjuAJC6ZPM8P
This was referenced Oct 27, 2026
…-47phpr Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JZRTRVa5FXXjuAJC6ZPM8P
Contributor
Author
席位核对 · ACCEPT · seat
|
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 Steve · project thread
Fixes #26190
Clause-②: yes (widening:
@object-ui/reactexports a newdelegationScopeMessage)Metadata-protocol: none
维护者速读
403 DELEGATION_SCOPE_VIOLATION),记录表单的 toast 与表单横幅、以及 flow 运行失败提示,改为显示服务端给出的原因;其它所有拒绝照旧显示固定的「没有权限保存」。PERMISSION_DENIED、FORBIDDEN、裸 403、行级安全拒绝的分支不变(A screen flow whose write node is refused shows the end user the engine's raw node text, in English, with the node id and the object API name ("Node 'create_account' failed: create_record(crm_account) failed: …") #24914 B′ 不变);回滚即 revert 本 PR。ui/packages/react/src/utils/error-message.ts(新函数delegationScopeMessage)、ui/packages/components/src/renderers/form/form.tsx(错误文案取值链)、ui/packages/app-shell/src/utils/flowResponse.ts(flowFailureMessage)。pnpm --dir ui exec vitest run packages/react/src/utils/error-message.test.ts packages/components/src/__tests__/form-write-error-message.test.tsx packages/app-shell/src/utils/__tests__/flowResponse.test.ts,预期 3 个文件全部通过。What changes
@object-ui/react: new exporteddelegationScopeMessage(err). It answers the server's reason, without the[Security]machine prefix (viaextractWriteErrorMessage), when the error's code isDELEGATION_SCOPE_VIOLATION(read fromcode, thendetails.code, the same pairisPermissionErrorreads, case-insensitive througherrorCodeIs). It answersnullfor everything else, and for a scope refusal with no usable text.isPermissionErroris unchanged and still answers true for any 403; its doc now says to ask the new reader first.@object-ui/components(record form): the message chain isdeclaredUserMessage→delegationScopeMessage→isPermissionError ? form.noPermissionToSave : server text. The toast and the in-form banner share the value.@object-ui/app-shell(flow run path,flowFailureMessage): the inner refusal is asked for the scope reason before the permission check, so a write node that met the same refusal shows the same reason..changeset/26190-delegation-scope-refusal-reason.md(patch on the three packages).git rm, and CI's Build & E2E UI regenerates the manifest.Tests
ui/packages/react/src/utils/error-message.test.ts:delegationScopeMessageanswers the reason for the client's 403 shape and fordetails.code; answersnullforPERMISSION_DENIED,FORBIDDEN, a bare 403, a row-level-security message andnull; answersnullfor a scope refusal with no text. One assertion pins why it must be asked first:isPermissionErroranswers true for the same error.ui/packages/components/src/__tests__/form-write-error-message.test.tsx: a scope refusal renders the reason without[Security]and without the fixed line; CONTROL: a plain403 PERMISSION_DENIEDkeeps the fixed line and withholds its text.ui/packages/app-shell/src/utils/__tests__/flowResponse.test.ts: the 400FLOW_FAILEDdoor and the 200 failed arm both show the reason; CONTROL on the same path: aPERMISSION_DENIEDinner refusal still readsform.noPermissionToSave.Acceptance notes
codeandmessagebut no status, and the scope reason does not matchisPermissionError's text pattern, so before this change the flow path already showed the server text, with its[Security]prefix. The ablation shows exactly that: with the new line removed the test fails on the prefix, not on the fixed line. The explicit read makes the rule hold by code rather than by the text happening not to match, and makes the two sites render the same string.userMessagemarking: not used. The ruled shape is the code;userMessagestill wins when a producer sets it, as before.delegationScopeMessageis a new export of@object-ui/react, so the declaration isClause-②: yes (widening), re-declared at the seat's review (the claim first saidno); the changeset carries the same line. Metadata-protocol staysnoneand the bump stayspatch.Verification
Head:
2f86b3a3(code);c786eccbchanges only the changeset'sClause-②line, and the changeset gates (check-adr-0087-registration,check-changeset-no-major,check-empty-changeset,check:changeset-gate-self-tests) exit 0 thereTests (lock VERDICT lines)
All runs on head
2f86b3a3(origin/main222755ebmerged in), underscripts/pm/os-verify-lock.sh:Ablations
Taken on head
43106bd8(pre-merge, before the record commits) withscripts/ablation-replace.mjs; tests resolve@object-ui/*tosrc/through the vitest alias, so no build sits between the mutation and the run.Gates (dispatch-gates --commands, 35 commands)
Derived by
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat2f86b3a3; every command run there; reconciled:CI owns the type-check lanes and the repo-wide scans.
Generated by Claude Code