Repository navigation
fix(console): a refused edit-form record read says a localized line, never the response body - #29322
Conversation
…never the response body Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jSXNWqmd9k2JHw7drz
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jSXNWqmd9k2JHw7drz
…mber measured Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jSXNWqmd9k2JHw7drz
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsR2jSXNWqmd9k2JHw7drz
|
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y Contract reviewServed-tier: Face: declaration. The PR body and the changeset declare ① Derived judgments
② Semver levelThe changeset entries are ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y 席位核对 · ACCEPT · 2026-11-02T12:19ZHead-sha:
Generated by Claude Code |
|
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y Landed · 2026-11-02T13:41Z · PR #29322 merged through the merge queue as
The card closed through Generated by Claude Code |
domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y
Requested by Steedos · project thread
Fixes #29186
Clause-②: yes (widening)
Metadata-protocol: none
维护者速读
/forms/NAME?recordId=ID)读取记录被拒时,横幅不再显示「Could not open record "ID" of OBJECT for editing (403): {…}」,改为一句本地化的话:无读权限(403 /PERMISSION_DENIED)显示「您没有权限查看这条记录。」,404 显示「这条记录不存在,或者您没有权限查看它。」(行级安全把看不见的行也答成 404,两者不可区分),其他失败显示「无法打开这条记录,请重试。」。FormPage.tsx的加载失败分支,复用 fix(console): a refused form submit says a localized sentence, never the server's response body #29184 的WriteRefusal/refusalText/refusalContent;十个语言包各加三个form键(@object-ui/i18n的发布面因此变宽,声明行按席位修正为yes (widening),changeset 仍为 patch)。回滚即还原此 PR。ui/apps/console/src/components/FormPage.tsx中refusalText的verb === 'Load'分支和加载器的throw new WriteRefusal('Load', …);FormPage.loadRefusal-29186.test.tsx。cd ui && pnpm exec vitest run apps/console/src/components/FormPage.loadRefusal-29186.test.tsx,预期 6 个用例全绿。What changes
ui/apps/console/src/components/FormPage.tsx, the ten i18n packs, tests and a changeset:recordIdbranch ofloadInternalFormno longer buildsCould not open record "ID" of OBJECT for editing (STATUS): BODY. It throwsnew WriteRefusal('Load', status, body): the parser PR fix(console): a refused form submit says a localized sentence, never the server's response body #29184 added for writes, its verb widened to'Load'and kept on the error asverb. No second parser. The raw body stays on.bodyfor a debugger.refusalTextreads a load on its own three lines, before any write rule, and never the server's text:404asform.recordNotFoundOrHidden, asked first, so a 404 whose text happens to name an access rule still reads as not-found-or-hidden (the text-sniffingisPermissionErrorwould otherwise tell the two apart);403,PERMISSION_DENIED,FORBIDDEN) asform.noPermissionToOpenRecord;form.openRecordFailed.form.noPermissionToSaveis not used for a load.WriteRefusalthroughrefusalText; every other load error keeps its message (the object-mismatch and blank-id refusals are unchanged).Measurement (showcase, before and after)
examples/app-showcaseonobjectstack dev --seed-admin --fresh, Console source on Vite, a member created by the admin (default member permission set). The before leg servedorigin/main'sFormPage.tsxfrom the same Vite; restored after, blob equal to HEAD./forms/showcase_contact.form?recordId=ID, member has no read right on contacts403{"error":"You do not have permission…","code":"PERMISSION_DENIED","object":"showcase_contact"}Could not open record "ID" of showcase_contact for editing (403): {"error":…,"code":"PERMISSION_DENIED","object":"showcase_contact"}(at 375 px the JSON runs off the banner)/forms/showcase_inquiry.contact?recordId=ID, the admin's inquiry, hidden from the member by the private OWD (the admin reads it 200)404{"error":"Record ID not found in showcase_inquiry","code":"RECORD_NOT_FOUND","object":"showcase_inquiry"}Could not open record "ID" of showcase_inquiry for editing (404): {"error":…,"code":"RECORD_NOT_FOUND",…}A missing id answers the same
404 RECORD_NOT_FOUNDshape as the hidden row (measured onshowcase_task), so the one line covers both. Screenshots, desktop and 375 px, before and after, are in the project files underissue-29186/.Tests
FormPage.loadRefusal-29186.test.tsx(6 cases; fixtures are the measured bodies): a 403PERMISSION_DENIEDread shows the denial line, and the page text has no{, no status, no record id and no object name, with no form or Submit left behind and no write issued; the same inzh; a 404RECORD_NOT_FOUNDshows the not-found-or-hidden line; a 404 whose text says "Access denied by row-level security policy" still shows that line; a 500 shows the generic line and not the driver text; a 502 markup page shows the generic line.FormPage.recordId.test.tsx: its three load-refusal pins asserted the old raw text (Could not open record "task-404".*404) and now assert the localized lines; what they guard (an error state, no create form, no write) is unchanged.FormPage.refusalText-29019.test.tsx(the write refusals) passes unchanged.scripts/ablation-replace.mjs, anchorelse if (e instanceof WriteRefusal) setError(refusalText(e, null, t));x1 -> x0, replaced by a banner of the status and the raw body): the new suite went 6/6 red; restored, blob equals HEAD andgit diff HEADis empty.Acceptance notes
loadInternalForm(form metadataCould not load form metadata (STATUS): view/NAME, the object-mismatch refusal) still draw their developer-facing message. They are outside this card's ruling (the record read); noted for the seat, not filed.Verification
Local subset. Head
8b71e4ae(branch merged withorigin/maintwice, both clean; the second merge brought an unrelated key into the same packs, so the i18n suite and the FormPage suites were re-run there).Tests
cd ui && pnpm exec vitest run --maxWorkers=2 apps/console/src/components/FormPage apps/console/src/components/InternalFormRoute apps/console/src/components/InAppFormRoute packages/i18n/onf4273fb0: Test Files 139 passed (139), Tests 1839 passed, 13 skipped.8b71e4ae:packages/i18n/plus the three FormPage load/write refusal suites, Test Files 98 passed (98), Tests 1469 passed, 13 skipped.pnpm exec turbo run type-check --filter=@object-ui/console --filter=@object-ui/i18n: 72 tasks successful (console'stype-checkcoverssrc, test files included).scripts/ablation-replace.mjsonFormPage.tsx, anchor x1 -> x0, the new suite 6 failed (6); restored, blob == HEAD,git diff HEADempty.Derived gates (`dispatch-gates --commands`, 34 families) on `7c4f91c8`, reconciled with `--ran`: 34 run, 0 NOT-MEASURED
Re-run on the final head
8b71e4aeafter the second merge (changeset, closing-keyword, manifest and ratchet families):Every other family is CI's: Lint UI, Type Check UI, Test UI and the rest of the required set on this PR.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QsR2jSXNWqmd9k2JHw7drz
Generated by Claude Code