Skip to content

fix(app-shell): a dashboard draft the server refuses to read stays unopened in Studio Interfaces, whatever order the designers load in - #29922

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-dawqbk
Nov 3, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-dawqbk

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #29884

Clause-②: no
Metadata-protocol: none

The two lines are copied from the claim (comment 6600806534).

维护者速读

  • 改了什么:Studio Interfaces 打开一个服务器拒绝读取(403)的仪表盘草稿时,不论设计器先加载还是后加载,都只显示拒绝说明,不再打开空白的属性表单、画布和项目工具(Source),自动保存也没有任何可发送的内容。
  • 为什么改:Studio: a dashboard draft whose requiredPermissions the author does not hold reads 403, Studio treats that as "no draft", and the next autosave overwrites the draft without the key #29015 的修复只在设计器先注册时成立。内置设计器是动态加载的,多数新打开的页面(以及"所有元数据"里的项目链接)首屏没有设计器,于是空草稿被当成已加载;之后在 Source 里改一下,就会无版本前置条件地覆盖草稿(实测 200),草稿的权限门和所有仅草稿的改动丢失,发布后仪表盘对所有人可见。18.3.0 挡版项的修复不完整。
  • 风险与回滚:只改拒绝读取(403)那一个分支,让它像对象、流程、应用三处已有的拒绝分支一样收回草稿归属。可读草稿、无草稿、其他读取失败的路径不变。回滚即撤销本 PR。
  • 先看:StudioDesignSurface.tsx 叶子加载里 'refused' in draftRead 分支新增的五行;新测试 describe(StudioDesignSurface.draftUnreadable-29015.test.tsx 末尾)。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design/StudioDesignSurface.draftUnreadable-29015.test.tsx,预期 6 个用例全部通过。
  • 需要你做的:无,席位落地。

What changes

  • Where the hole was. The built-in designers register through a dynamic import, so the Interfaces pillar's first render can find no dashboard preview (isEditable = !!Preview && !StudioCanvas is false). That render takes the no-designer arm of the leaf load, which installs the leaf's clean {} buffer and stamps it as the leaf's (setDraftFor(leafKey), objectui#11272). When the preview registers, the load runs again and reads the draft. On a 403 its refused branch (added by fix(app-shell): Studio opens no buffer over a dashboard draft the server refuses to read #29132) only set the strip's error and returned, so the stamp stayed. To useDraftAutoSave a stamped buffer is a loaded one, so the canvas, the Properties inspector and the item tools (Source) opened on {}, and the autosave was live with no version pin. A reload of a leaf whose earlier read was answered (a publish, a rollback) kept its loaded buffer the same way.
  • The fix. The refused branch now takes the leaf's buffer back: setDraft({}), setDraftFor(''), forgetLeafVersion(), the read address cleared, setIfDirty(false). The object (setObjDraftFor('')), flow (setDraftFor('')) and app nav (setAppDraftFor('')) refused branches already do this.
  • The no-designer arm is kept as it is. objectui#11272 needs that leaf's clean {} buffer. While !isEditable, the autosave is blocked (blocked: !current || !isEditable || …) and the inspector needs isEditable, so the arm sends nothing by itself. The hole was only the refusal keeping its stamp.
  • Census of the other editors this surface hosts. The object and flow loads have no pre-registration arm that stamps ownership (both effects start at if (!current) return), and their refused branches, like the app nav's, already clear. Only the Interfaces leaf had this ordering.
  • The "All metadata" deep link is the same load. /studio/PKG/metadata/dashboard/NAME navigates a dashboard to the Interfaces pillar (StudioMetadataArea → areaItemPillarPath), so it is covered by the same fix, not a second editor.

Tests

A new top-level describe in the #29015 shared harness, StudioDesignSurface.draftUnreadable-29015.test.tsx. The designer registries are module state with no unregister, and the file's beforeEach registers the dashboard preview, so each late-registration case loads the pillar and its registries afresh (vi.resetModules), with nothing registered. Each case first asserts the stamped state (the Source tool is shown and the draft has not been read), then registers the preview and a default inspector inside act.

  • Interfaces route, late registration: the strip with the server sentence shows; no canvas, no inspector edit, no Source tool; no save after the autosave window; the stored draft still carries requiredPermissions.
  • The area's deep link: the same, mounted at the URL areaItemPillarPath gives for /studio/PKG/metadata/dashboard/NAME. This mounts the landing the area navigates to, not the area route itself.
  • Same-leaf reload: a readable draft opens, then a publishNonce reload is refused; the loaded buffer is taken back and nothing is sent.
  • CONTROL, readable draft after late registration: it opens editable, and an inspector edit saves the served draft (its requiredPermissions included) pinned ifMatch: 'v1'.
  • CONTROL, early registration: the existing Studio: a dashboard draft whose requiredPermissions the author does not hold reads 403, Studio treats that as "no draft", and the next autosave overwrites the draft without the key #29015 case (preview registered in beforeEach) still refuses, unchanged.
Red at main, green with the fix, ablation
Neighbour sweep and type-check (head 1a9786f, after merging main)
  • Every ui test file naming InterfacesPillar (57 files, the harness included), two locked batches at --maxWorkers=2: 29 files / 155 tests passed; 28 files / 283 tests passed.
  • Narrowing, declared: the 223 files naming StudioDesignSurface are not all run locally; the touched code is the Interfaces pillar's leaf load, and CI's Test UI runs the rest.
  • pnpm exec turbo run type-check --filter=@object-ui/app-shell (65 tasks, test files included): exit 0.
  • eslint on the two touched ui files: 0 errors; the 36 warnings all predate this PR (none is on a line it adds).

Acceptance notes

  • Live, card steps 1–7 on a fresh showcase backend and the Console Vite dev server: a writable package, a published dashboard with one metric widget, an app whose nav names it, and a draft with requiredPermissions: ['showcase.export_data'] (admin's draft read: 403).
    • Before (main's StudioDesignSurface.tsx served): 7 of 7 fresh loads (5 on Interfaces, 2 by the deep link) show the strip AND the empty Properties form (5 form controls) and the item tools. A Source edit to the published body sent PUT …?mode=draft&package=…&surface=studio with no If-Match, answered 200, and admin's draft read flipped 403 → 200 (the gate was gone). That happened on both routes.
    • After (this branch): 9 of 9 fresh loads (5 + 2 loads, plus the two edit attempts) show only the strip, with 0 form controls, no item tools and no PUT. The draft read stays 403.
    • The swap was restored (blob == HEAD), and the after leg was re-run once Vite served the restored file again.
  • Probe script, JSON readings and screenshots: /mnt/project-files/issue-29884/shots/.
  • Fixes #29884 is on the third line, after the project attribution block, which the session requires as the body's first two lines.

Verification

Local gate union on the final head 58c3a3f7 (main merged twice; the second merge brought 5 backend-only commits, none touching ui/):

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 35 families for the 3 changed paths (182 changed lines).
  • The 34 non-vitest families: all exit 0 on 58c3a3f7, among them check-sdui-manifest.mjs (exit 0, the record does not move for a logic-only edit), check-changeset-no-major, check-empty-changeset, check-adr-0087-registration, check:query-options-erasure, check:type-check-debt, check:cross-package-test-inputs, check:test-source-alias.
  • pnpm --dir ui exec vitest run scripts/: on 1a9786f9 (the first merge), Test Files 170 passed | 2 skipped (172), Tests 4979 passed | 2 skipped (4981), exit 0. The re-run on 58c3a3f7 finished green after this PR opened (4979 passed; card report 6604287278). The second merge touched no ui/ file.
  • dispatch-gates --ran on 1a9786f9: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN.
  • CI runs the rest: Type Check UI, Test UI, Lint UI, Build & E2E UI and the required set.

Generated by Claude Code

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 3, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m

席位核对 · ACCEPT · 2026-11-03T13:04Z

Head-sha: 58c3a3f7d94e3216a4ae43e270bcfe98067f7ca0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

1 participant