Skip to content

fix(data-objectstack): a plain list read of a missing object shows the server's 404 code and sentence, not an empty list - #29839

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

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-uqmcy3

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #29782

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:对象列表的普通读取(没有展开查找字段、也没有搜索)如果被服务器回复 404,而这个对象不在平台对象清单里,现在会把错误抛给页面,页面显示服务器的原句和错误码(如 OBJECT_NOT_FOUND),不再显示「这里还没有数据」。平台自带的可选集合(如 sys_presence、sys_activity)在未安装时仍然安静地当作空集,并且只请求一次。
  • 为什么改:对象注册失败时(Studio in zh-CN: a new object publishes "successfully" and never reaches the data plane, because its echoed queue column carries a translated label #29673),服务器已经明确说了「对象不存在」,列表却说「这里还没有数据」,让搭建者以为对象只是空的。Studio: the publish sheet and load errors speak in plain words, and a load error carries the server's real reason #29704 只修好了带展开或搜索的读取路径,普通路径仍然把 404 吞掉并记住,之后每次都直接返回空。
  • 风险与回滚:影响面比单个列表宽。凡是读取「自己那个对象」的界面,在对象缺失时都会从空状态变成加载失败状态:列表、看板、日历、甘特、画廊、时间线、地图、图表、仪表板组件、相关列表及其计数、查找与收件人选择器、报表等。仪表板指标在对象缺失时不再显示 0,而是显示错误。平台可选集合的行为不变。只改界面适配器,不改接口和元数据协议。回滚只需还原本 PR。
  • 先看:ui/packages/data-objectstack/src/index.ts 中 find 的 404 分支(新增 isPlatformProvidedObjectName(resource) 条件),以及新测试 ui/packages/data-objectstack/src/findMissingObject-29782.test.ts。
  • 两分钟验证:中文界面下打开「功能展示」应用的「客户」列表,用浏览器开发者工具把 /api/v1/data/showcase_account 的请求改为返回 404 {"success":false,"error":{"code":"OBJECT_NOT_FOUND","message":"Object 'showcase_account' is not registered"}},应看到「无法加载记录」、服务器原句、OBJECT_NOT_FOUND 和「重试」,而不是「这里还没有数据」。
  • 需要你做的:无,席位落地。

What changes

  • ObjectStackAdapter.find, plain SDK path: a 404 that is not an enable-block denial now degrades to { data: [], total: 0 } and enters the missingResources memo only when isPlatformProvidedObjectName(resource) (@objectstack/spec/system, the curated platform registry) is true. Any other object's 404 is rethrown as the client threw it (httpStatus, code, message) and is not memoised, so the next read asks the server again.
  • The error code cannot make this split: an uninstalled optional collection such as sys_presence and a list's own unregistered object both answer 404 OBJECT_NOT_FOUND. The split is by name, against the registry, never a prefix guess. The short-circuit at the top of find can now only ever hold platform names.
  • enable-block denials (OBJECT_API_DISABLED, OBJECT_API_METHOD_NOT_ALLOWED) are rethrown and unmemoised as before. The raw $expand / $search path is unchanged.
  • No surface code changes: the list already renders an answered failure with the server's sentence and code since Studio: the publish sheet and load errors speak in plain words, and a load error carries the server's real reason #29704 (answeredLoadFailure).

Why

The memo exists for optional platform collections, whose absence means "this deployment does not install the feature". It also swallowed the 404 of a list's own object, so an object that failed to register (#29673) read as 这里还没有数据, and only the list's expand read reached the error panel.

Caller census (ui src at the base, tests excluded). Every literal or constant find target in ui/ is in the registry (sys_activity, sys_presence, sys_comment, sys_comment_reaction, sys_audit_log, sys_attachment, sys_user, sys_user_position, sys_user_permission_set, sys_user_preference, sys_member, sys_personal_view, sys_approval_action, sys_notification_receipt, sys_inbox_message, sys_metadata, sys_environment). The non-registry sys_ literals (sys_favorite, sys_claim, sys_view, sys_dashboard, …) are action names or update targets, never find targets. Every non-platform caller that relied on the empty answer reads its own surface's object, and now shows its existing load-failure state on a missing object.

Tests

  • New ui/packages/data-objectstack/src/findMissingObject-29782.test.ts:
    • a plain find on a non-registry object answered 404 OBJECT_NOT_FOUND rejects with that code, httpStatus 404 and the server sentence;
    • a second find issues a second request (no memo);
    • a bare 404 with no code on a non-registry object is rethrown too;
    • over the real @objectstack/client: the wrapped { success: false, error: { code, message } } 404 reaches the caller as code + httpStatus, twice, two requests;
    • CONTROL: a registry name (sys_presence) answered 404 OBJECT_NOT_FOUND answers empty and the second call is short-circuited (one request);
    • CONTROL: the memo is per resource;
    • CONTROL: an enable-block denial still rethrows, on a platform name and on any other, and is not memoised;
    • CONTROL: a 2xx with zero rows on a non-platform object is still an ordinary empty result.
  • ui/packages/app-shell/src/views/objectListApiDisabled-4408.test.tsx (the shared "real ObjectStackAdapter feeding the real ListView" harness): a new top-level describe for data-objectstack: a plain list read answered 404 OBJECT_NOT_FOUND reads as an empty list ("no data yet"), so the list never shows the server's reason #29782. A list over crm_invoice answered 404 OBJECT_NOT_FOUND shows the server sentence and the list-error-code chip, with no empty state and no network sentence, and every data read it made took the plain path. The harness's makeAdapter keeps a handle on its fetch stub (ds.__fetch) for that last check. Its existing bare-404 control on sys_jwks stays green, since sys_jwks is a registry name.

Moved pin

  • ui/packages/data-objectstack/src/apiAccessDenied.test.ts, "CONTROL: a 404 RECORD_NOT_FOUND is not an enable-block denial": its resource moves from account to sys_comment. Its point is that RECORD_NOT_FOUND is not an enable-block denial, so it still takes the optional-collection branch. On account, a non-registry object, the 404 is now rethrown by design. That case is pinned in the new file.

Ablation (one-shot, not kept): scripts/ablation-replace.mjs deleted the && isPlatformProvidedObjectName(resource) line (anchor 1 to 0, blob f82eb0b7 to 8dd44be3). The new adapter file and the new ListView describe went red: 6 tests failed, 8 passed, including "REJECTS a plain find answered 404 OBJECT_NOT_FOUND…". The file was restored to the HEAD blob f82eb0b7, and git diff HEAD came back empty. The ui vitest aliases @object-ui/data-objectstack to src, so no rebuild leg was owed.

Acceptance notes

  • The analytics fallback aggregateViaFind reads through find, so a dashboard figure over a missing object now fails instead of reading 0. This is the intended direction, and it is named in 风险与回滚.
  • The live run saw no sys_presence read on the screens it visited (RecordDetailView's comment records that its presence probe was retired). So the shell's boot-time plain reads of sys_activity and sys_inbox_message served as the live optional-collection control.

Verification

All readings below were taken at head 6834710a (this branch merged with origin/main b0ef25f) unless a different head is named.

Unit and consumer tests (locked, maxWorkers=2)
  • packages/data-objectstack/ (114 files), the plugin-list ListView.loadErrorKind, and the 22 test files outside the package that build a real ObjectStackAdapter and answer 404 (including objectListApiDisabled-4408): 137 files, 1530 tests passed at 6834710a.
  • Before the merge of main, at a9f52f3e: the four touched files gave 50/50, and the same consumer set gave 135/135.
  • pnpm exec turbo run type-check --filter=@object-ui/data-objectstack --filter=@object-ui/app-shell: 66/66 tasks. data-objectstack's program includes its tests, and app-shell runs tsconfig.test.json.
  • eslint on the four touched ui files: 0 errors.
Derived gate union (dispatch-gates --commands, 36 families)
  • At 6834710a: every one of the 36 exited 0, including node scripts/check-sdui-manifest.mjs (and --self-test), pnpm --dir ui exec vitest run scripts/ (170 files passed, 2 skipped), pnpm check:undeclared-dep-imports, pnpm check:cross-package-test-inputs, pnpm check:test-source-alias and pnpm check:query-options-erasure. dispatch-gates --ran: 36 derived, 36 run, 0 NOT-MEASURED.
  • The same 36 also exited 0 at a9f52f3e, before the merge.
  • Tier: no path-derived mandate. Changed lines are under the 3000 threshold.
Live run (showcase backend + Console Vite, zh-CN, Playwright)
  • Script: node shots.cjs before|after against pnpm dev -- --fresh and the Console Vite proxied to it. Every /api/v1/data/showcase_account read is fulfilled with 404 { success: false, error: { code: 'OBJECT_NOT_FOUND', message: "Object 'showcase_account' is not registered" } }. This list reads only on the plain path: ?top=0 and ?top=50&select=…, with 0 raw reads.
  • Before (adapter swapped to the origin/main copy, blob 7ddcc01a): no error state. The list shows 这里还没有数据 and a · 0 count.
  • After (this branch): the error state reads 无法加载记录 · Object 'showcase_account' is not registered · OBJECT_NOT_FOUND · 重试, with no 这里还没有数据. It was re-probed after the source was restored (blob back to HEAD f82eb0b7), with the same reading.
  • CONTROL (both legs): sys_activity, sys_inbox_message and sys_notification_receipt answer 404 OBJECT_NOT_FOUND. After boot and four SPA navigations (home, contacts, home, products, home), the Console made exactly 1 request each for sys_activity and sys_inbox_message (sys_notification_receipt was not read on this path), and showed 0 error panels or alerts.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic.
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes. (eslint on the touched files: 0 errors; the repo-wide run is CI's.)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset (.changeset/29782-missing-object-list-read.md, patch), and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: n/a, not touched.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the 维护者速读 block is the pm-dispatch house section).

Generated by Claude Code

… of reading as an empty list

The plain find path degraded every 404 to an empty result and memoised the
resource. The empty answer and the memo now apply only to names in the
platform registry (isPlatformProvidedObjectName); any other object's 404 is
rethrown with its code and sentence and not memoised.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jUqz12Aiuvrkaw5SJa
…rror panel over the real adapter

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jUqz12Aiuvrkaw5SJa
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Nov 3, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m

席位核对 · ACCEPT · 2026-11-03T07:13Z

Head-sha: 6834710af424633e5c8dad1c771870e407706b70

  • Shape: draft → main, and Fixes #29782 is the only closing keyword in the body. 5 files, +258 / −9. check-governed-merges --pr 29839 reports NOT governed, at 267 lines, so this is an ordinary queue landing. Clause-②: no and Metadata-protocol: none are line-initial in the body and the changeset, as in claim 6595989616. No contract review is owed: neither leg hits, since this narrows no published accept-set and adds no export. The changeset is a patch for @object-ui/data-objectstack.
  • File surface: inside the claim, as accepted in the card thread. objectListApiDisabled-4408.test.tsx is the shared real-adapter-over-ListView harness, and gets one helper line plus a new describe. The serial on PR feat(app-shell): the publish sheet speaks the console's language, and a load error the server answered shows its code and sentence #29780 is cleared: it is on main (69dbb3cc).
  • Read in the diff:
    • find's plain-path 404 degrades to empty and enters missingResources only when isPlatformProvidedObjectName(resource) holds. That is the curated registry from @objectstack/spec/system, not a prefix guess. ui/ already imports that entry, and @objectstack/spec declares sideEffects: false.
    • Every other 404 is rethrown as the client threw it, and is not memoised. Enable-block denials are unchanged, and the raw $expand/$search path is unchanged.
    • The short-circuit at the top of find can now hold only registry names.
  • Moved pin: apiAccessDenied.test.ts's "404 RECORD_NOT_FOUND is not an enable-block denial" control moves from account to sys_comment, with a comment. Its assertion is unchanged, and the non-registry case is pinned in the new file.
  • Census (dev, in the card thread): every literal or constant find target in ui/ is a registry name, and no optional probe uses a non-registry name. The non-platform callers that relied on the empty answer all read their own surface's object, and now show their load-failure state. That breadth is stated in the 速读 风险与回滚 line.
  • Tests (dev, report 6597452885): 137 files and 1530 tests are green, including all 22 out-of-package real-adapter 404 files, none of which went red. The registry-check ablation reds 6 and was restored. Type-check passes 66/66. Of 36 derived gates, all 36 exit 0, with 0 NOT-MEASURED.
  • Live (zh-CN): before, the screen read 这里还没有数据. After, it reads 无法加载记录 with the server sentence, OBJECT_NOT_FOUND and a retry. CONTROL: sys_activity and sys_inbox_message answering 404 were each asked once across boot plus 4 navigations, with no error panel. Shots are in /mnt/project-files/issue-29782/shots/.
  • Gates (check-runs on this head): 44 success, 11 skipped (opt-in and schedule gates), 0 failed, 0 pending. Lint & Repo Gates and TypeScript Type Check both succeeded. mergeable_state is clean.
  • Out of scope: none reported. Nothing to file.
  • Landing: pr_ready, then automerge_enable. Merging closes data-objectstack: a plain list read answered 404 OBJECT_NOT_FOUND reads as an empty list ("no data yet"), so the list never shows the server's reason #29782.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 3, 2026 07:15
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 3, 2026 07:15
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit 68e26c4 Nov 3, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-uqmcy3 branch November 3, 2026 07:33
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 tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

data-objectstack: a plain list read answered 404 OBJECT_NOT_FOUND reads as an empty list ("no data yet"), so the list never shows the server's reason

1 participant