Skip to content

fix(app-shell): a draft-only object's nav entry opens its drafted default list view in Studio Interfaces - #30315

Draft
objectstack-fleet[bot] wants to merge 3 commits into
mainfrom
claude/project-thread-ldhemo
Draft

objectstack-fleet[bot] wants to merge 3 commits into
mainfrom
claude/project-thread-ldhemo

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Yibo Wang · project thread

Fixes #30177

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:Studio 界面里,一个还只是草稿的对象,从导航入口打开时现在会打开作者草拟的那个默认列表视图(例如「All deals」),视图下拉里不再多出一个并不存在的「Default view」,第一次编辑也写进这个视图本身。
  • 为什么改:之前入口只按已发布的元数据找默认视图,草稿对象不在其中,于是打开不存在的 OBJECT.default;作者一编辑,包里就多出第二个默认列表视图,而且它的列是空的。
  • 风险与回滚:只改 Studio 面板选哪个视图,不动接口、数据和规格。已发布对象的行为不变;包的草稿列表读回来之前,面板对「尚未标记默认的对象」多转一下加载圈。回滚即还原本 PR。
  • 先看:ui/packages/app-shell/src/views/studio-design/ObjectListViewInspector.tsx 里 useObjectListViewDraft 的 entryTarget / holding 两段,以及新函数 draftedListViewsOf。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design/ObjectListViewInspector.draftOnlyObject-12508.test.tsx,预期 8 个测试全部通过,其中 objectstack#30177 一组断言入口打开 acmetrial_deal.all、切换器只列这一项、第一次编辑只写这一个视图。
  • 需要你做的:无,席位落地。

What changes

In Studio › Interfaces, the list-view panel of an object nav entry with no viewName edits the object's default list view. useObjectListViewDraft resolved that view (listViewTargetOf) from the shell's merged listViews only. A draft-only object is absent from the shell's metadata, so the entry opened OBJECT.default, which does not exist; the header's view switcher listed that "Default view" beside the view the author had drafted with isDefault: true; and the first edit created OBJECT.default with isDefault: true, a second default list view in the package (seeded with no columns).

  • The entry's default is now resolved against the merged views and this package's drafted list views of the object (draftedListViewsOf, over the draft-overlaid view list the switcher already reads; no new request). A flagged merged view still wins; a flagged drafted view comes next; OBJECT.default stays the fallback.
  • That draft read is asynchronous. Until it answers, an entry whose default no merged view flags is held (holding): no buffer is loaded and nothing is listed, so no edit can be made against the fallback name before the drafted default is known. A failed or refused draft read lists nothing drafted, and the entry falls back to OBJECT.default as before.
  • This applies to every object, not only draft-only ones. The draft-only set (draftOnlyObjects) is itself read asynchronously and can be stale across a publish, and a published object whose package drafts its first default view had the same phantom; resolving against the drafted views avoids both without depending on that set.

Changeset: @object-ui/app-shell patch.

Tests

New top-level describe in the existing hook harness ObjectListViewInspector.draftOnlyObject-12508.test.tsx (real hook, real MetadataClient over an in-memory server; the server now also serves the drafts ledger, the draft-overlaid view list, ?state=draft reads and records PUTs):

  • draft-only object + drafted acmetrial_deal.all (isDefault: true) + entry with no viewName → the panel targets and loads acmetrial_deal.all; the switcher lists only it; the first edit (made as the panel makes it) PUTs view/acmetrial_deal.all and no other view carries isDefault;
  • while the draft-overlaid view list is held, nothing is loaded or listed and no acmetrial_deal.default request is sent; once it answers, the entry is acmetrial_deal.all;
  • CONTROL: a published object with no flagged view still targets OBJECT.default;
  • CONTROL: a draft-only object with no drafted view still targets OBJECT.default, and the first edit creates it flagged default.
Ablation (fix committed first; scripts/ablation-replace.mjs, restored to the HEAD blob, git diff HEAD empty)
  • Draft read removed ({ ...publishedViews, ...draftedListViewsOf(objectName, draftViewRows) } → publishedViews): 2 failed, 6 passed. The first pin reads viewId acmetrial_deal.default where acmetrial_deal.all is expected; the hold pin's settled target is acmetrial_deal.default.
  • Hold removed (holding forced false): 1 failed, 7 passed. loadedFor reads listView:acmetrial_deal.default while the drafts are unread.
Test runs
  • At d0d0f1e6 (before merging main): every app-shell test file importing ObjectListViewInspector or StudioDesignSurface, 210 files, --maxWorkers=2, under the verify lock: Test Files 210 passed (210), Tests 1344 passed (1344), VERDICT command-exit 0.
  • At bfd2fbff (after merging main 959c26be, rebuilt): the 33 list-view, Interfaces-pillar and draft-only files of that set: Test Files 33 passed (33), Tests 183 passed (183), VERDICT 0. pnpm --filter @object-ui/app-shell run type-check: VERDICT 0.
  • eslint on the two touched source/test files from the package dir: 0 errors (22 warnings, all pre-existing patterns on lines this PR does not touch).
Gates at bfd2fbf

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (3 paths vs merge base 959c26be1): 35 commands, run serially under one verify lock (VERDICT command-exit 0), every one exit 0, including node scripts/check-sdui-manifest.mjs and pnpm --dir ui exec vitest run scripts/ (Test Files 170 passed | 2 skipped (172), Tests 4985 passed | 2 skipped). --ran with the recorded exit codes: 35 derived, 35 run, 0 NOT-MEASURED (a derived zero). The record is the project's ui-orders/30177-gates-ran.list.

Not run locally: the full ui suite (CI's Test UI shards run it). origin/main moved after the merge; its only overlap with this PR's area is four added lines in StudioDesignSurface.tsx, a file this PR does not edit.

Verification

Dev boot of examples/app-crm (objectstack dev --seed-admin --fresh) after a full build, console Vite pointed at it, Playwright Chromium, signed in as the dev admin. Seeded over the REST door as the card describes: writable package com.acme.trial, then with ?mode=draft the object acmetrial_deal, the list view acmetrial_deal.all (label: All deals, isDefault: true) and the app acmetrial_app whose one nav entry is the object with no viewName. Opened the entry in Studio › Interfaces.

Before (main's ObjectListViewInspector.tsx, Vite restarted with --force, served module checked) After (this branch)
Panel opens acmetrial_deal.default, "There is no list view "Default view" yet", 0 columns acmetrial_deal.all, "Unpublished draft", columns Name, Stage
Switcher lists Default view ✓, All deals All deals ✓
First edit (Add field → Amount) PUTs view/acmetrial_deal.default, isDefault: true, columns [amount] view/acmetrial_deal.all, isDefault: true, columns [name, stage, amount]
Package's view drafts afterwards acmetrial_deal.all and acmetrial_deal.default, both isDefault: true acmetrial_deal.all

Shots (2x, 1440 wide) and the request readings are in the project's ui-orders/30177-shots/ folder (before-switcher.png, before-edited.png, after-switcher.png, after-edited.png); this repository has no place to commit them, per ui/AGENTS.md Housekeeping.

Acceptance notes

  • Create View / first-edit column seed for a draft-only object, not fixed here. seedColumns is defaultListColumnsFromObject(objectDef, 5) and objectDef comes from the shell's merged objects, which do not hold a draft-only object, so the seed is empty: the before leg's first edit wrote columns: [amount] only, and New view's fallback columns are empty the same way. Seeding from the object's draft fields is a different read (the object's draft definition, not the view drafts this PR reuses), so it stays out of this PR per the dispatch and is reported to the seat for its own card.
  • While the package's view drafts are unread, the header title of an object with no flagged merged view still names the fallback view ("Default view") for that moment; the switcher lists nothing and the panel shows its loading state, so nothing can be edited.

Generated by Claude Code

…ault list view in Studio Interfaces

The entry of an object with no viewName resolved its default list view from
the shell's merged listViews only. A draft-only object is absent there, so
the entry opened a non-existent `<object>.default`, the view switcher listed
a phantom "Default view" beside the drafted one, and the first edit created
a second default list view.

The entry's default is now resolved against the merged views and this
package's drafted list views (the draft-overlaid list the switcher already
reads). Until that read answers, a default no merged view flags is held: no
view is loaded or listed for it.

Claude-Session: https://claude.ai/code/session_01WGufzigMc8dMgZMjwQtjCq
Co-authored-by: Claude <noreply@anthropic.com>
…et for the draft-only default list view fix

Claude-Session: https://claude.ai/code/session_01WGufzigMc8dMgZMjwQtjCq
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · os-support-ai · session_01WGufzinedUGSXteN45poyo

维护者速读

  • 改了什么:Studio 界面里只存在于草稿的对象,从导航入口打开时会进入作者草拟的默认列表视图(如「All deals」);视图下拉不再多出不存在的「Default view」,第一次编辑写进该视图本身。
  • 为什么改:入口以前只按已发布元数据找默认视图,草稿对象不在其中,于是打开 OBJECT.default;一编辑包里就多出第二个默认列表视图(Studio Interfaces: a draft-only object's nav entry opens a non-existent 'Default view', and the first edit there creates a second default list view #30177)。
  • 风险与回滚:只改 Studio 面板选哪个视图,不动接口、数据和规格;已发布对象行为不变;草稿列表读回前,未标默认的对象多转一下加载圈(读失败也会结束等待)。回滚即 revert。
  • 先看:ObjectListViewInspector.tsx 的 draftedListViewsOf 与 entryUnsettled / holding。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/studio-design/ObjectListViewInspector.draftOnlyObject-12508.test.tsx(8 条全绿)。
  • 需要你做的:CI 全绿后请 Ready + 加入合并队列(或回复「允许落地」由我代办)。

席位意见:ACCEPT。

席位核对

ACCEPT → PR #30315 @ bfd2fbffbd322204f0a0109fce9e2bc8efb36184 (card #30177, claim 6649605234, os-dev-report 6650893184).

  • Shape: draft, base main, Fixes #30177. 3 files, +190/−14; check-governed-merges --pr 30315: NOT governed (0 of 3 paths), under the human-merge threshold.
  • Seat diff read: entryTarget now resolves over { ...publishedViews, ...draftedListViewsOf(objectName, draftViewRows) }, where the drafted rows come from the draft-overlaid view list the switcher already reads (no new request). entryUnsettled holds the buffer load and the switcher list only while (a) the target is a default, (b) the draft read has not answered for this [packageId, objectName], and (c) no merged view flags isDefault. A published object with a flagged default never holds. The draft read's rejection path also sets for: forKey, so a refused read ends the hold and the panel falls back to <object>.default as before. A user pick (pickedViewId) is never held.
  • Tests: 4 new pins in ObjectListViewInspector.draftOnlyObject-12508.test.tsx (drafted default targeted, listed alone, first edit writes it; hold until drafts answer; CONTROL published object with no flag → OBJECT.default; CONTROL draft-only object with no drafted view → OBJECT.default, created by the first edit). File 8/8. Ablation: draft read removed → 2 red; hold forced false → 1 red; controls green. 210-file app-shell sweep 1344/1344 (pre-merge), 33 files 183/183 after merging main; type-check and eslint clean.
  • Browser: before/after on a dev boot, ui-orders/30177-shots/ (before: entry opened acmetrial_deal.default, switcher listed Default view + All deals, first edit PUT a second isDefault view; after: opens acmetrial_deal.all, lists it alone, edit writes it).
  • Gates: dispatch-gates --commands 35 exit 0, --ran 35/0 NOT-MEASURED. CI on this head: in progress at this record.
  • Out-of-scope finding: filed Studio Interfaces: on a draft-only object, a new or first-edited list view starts with zero columns #30317 (draft-only object seeds list-view columns from nothing: seedColumns reads the merged objectDef, which omits a draft-only object). Not in this PR.
  • Serial: [Decision] Selection-bar actions cannot be configured in Studio: should the view panel offer them? #30089 (claimed, building) edits an additive region of the same file. Whichever lands second merges main; the regions are disjoint.
  • Scope: changeset @object-ui/app-shell patch, Clause-②: no, Metadata-protocol: none. No security findings.
  • Landing: when every check is green or expected-skip, relay pr_ready + automerge_enable. On MERGED: landing note on Studio Interfaces: a draft-only object's nav entry opens a non-existent 'Default view', and the first edit there creates a second default list view #30177, remove pm:dispatched.

This branch has not been deployed

No deployments
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Studio Interfaces: a draft-only object's nav entry opens a non-existent 'Default view', and the first edit there creates a second default list view

1 participant