Skip to content

fix(metadata-protocol): getUiView 不再多发三个未声明键,响应体与 GetUiViewResponseSchema 对齐 (#5948) - #6271

Merged
qq9340100 merged 1 commit into
mainfrom
claude/issue-5948-getuiview-slim-body
Aug 7, 2026
Merged

fix(metadata-protocol): getUiView 不再多发三个未声明键,响应体与 GetUiViewResponseSchema 对齐 (#5948)#6271
qq9340100 merged 1 commit into
mainfrom
claude/issue-5948-getuiview-slim-body

Conversation

@qq9340100

Copy link
Copy Markdown
Collaborator

Fixes #5948

执行 2026-08-07 维护者重裁:选项 3(前提证伪后取代 03:19 的裁 A 字面落点)—— 生产端摘掉多发键,packages/spec 零改动,changeset patch

分支改名说明:认领时的分支名是 claude/issue-5948-get-view-response-union,描述的是被证伪的裁 A 方向(改 GetViewResponseSchema 为判别联合)。重裁为选项 3 后该名称已名不副实,按语义改为 claude/issue-5948-getuiview-slim-body;原分支未推送过,无遗留。

一、前提修正(为什么落点和原单不同)

原单与裁 A 都称这条路由的 spec 声明是 GetViewResponseSchema —— 实测是张冠李戴,已在 issue 证据评论 逐条留档:

响应 schema 接口 生产实现 REST 路由
getUiView GetUiViewResponseSchema(protocol.zod.ts:363)= ViewSchema :1533 protocol.ts:4179 GET /ui/view/:object/:type
getView GetViewResponseSchema(:702)= {object, view} :1560 无 ❌ 无 ❌

rest-server.ts:4895-4919 调的是 p.getUiViewres.json(view)GetViewResponseSchema 属于零实现零路由的 View-Management CRUD 块(已另立 #6239,维护者裁退役、排 #6083 后)。本 PR 不碰 spec,也不碰那五个休眠声明。

真实缺陷在成员层:ViewSchema 容器本就声明 list? / form?,但成员内多发的三个键被 strictObject 判红。

二、判别键与真实形状(实测,非推测)

两条 return 的顶层键分别只有 list / form,无公共字面量判别键 —— 严格意义的 z.discriminatedUnion 在顶层不适用。这也是选项 3 更顺的原因之一:容器 ViewSchema 早已用「可选成员」表达了这个二选一,无需新建联合。

改前实测(红色基线)

GetUiViewResponse  ..  真实 list body: RED
   [unrecognized_keys] path=["list"] Unrecognized key(s) on this list view: `object`.
GetUiViewResponse  ..  真实 form body: RED
   [unrecognized_keys] path=["form"] Unrecognized key(s) on this form view: `object`, `label`.

三、FROM → TO

FROM  { list: { type, object, label, columns, sort, searchableFields } }
TO    { object, list: { type, label, columns, sort, searchableFields } }

FROM  { form: { type, object, label, sections } }
TO    { object, form: { type, sections } }

「原来当 unknown 用的代码怎么改」:此前 client.meta.getView 返回 unknown,调用方要么不读、要么 as any 强读。改后 body 满足 GetUiViewResponseSchema,即 View:

  • object 的:body.list.object / body.form.objectbody.object(相同的值上移一层,不是删除 —— ViewSchema 一直在容器层声明 object:「Object this container binds to」);
  • 读 form 标题的:body.form.label(原 `Edit ${…}`)已摘除且不上移 —— 渲染串不属于元数据,任何 view schema 都没声明过它;调用方本就知道自己请求的对象,标题由 UI 自行拼;
  • list.label 的:不受影响,ListViewSchema 正式声明了 label,保持原样。

消费面实测(定级 patch 的依据):client-reactuseView(metadata-hooks.tsx:229)把 body 当 any 透传(UseMetadataResult.data: any);objectui 全仓 meta.getView 零命中(其 getView(objectName, viewId)client.meta.getItem('view', …),另一条通路)。无编译期破坏面,无类型改判 ⇒ patch

未验面:cloud 仓不在本次验证范围(按 #5540 口径如实标注)。若该仓直读 body.list.object / body.form.object,按上面上移一层。

四、常驻 pin 与反向验证

packages/metadata-protocol/src/protocol.ui-view-response-conformance.test.ts —— 刻意用生产端真实组装路径(实调 getUiView)喂 GetUiViewResponseSchema.safeParse,而不是手拼 fixture:手写 fixture 钉的是「本文件相信生产端发什么」,而那个相信恰恰就是此前错的东西。

反向验证方向事先声明:本例是常规「红」向 —— pin 断言的是 full safeParse GREEN 且 schema 为 strictObject,故恢复任一多发键必然产出 unrecognized_keys 并点名。实跑结果与预测一致:

× list branch parses GREEN …   → Received: "[unrecognized_keys] path=[\"list\"] … `object`."
× form branch parses GREEN …   → Received: "[unrecognized_keys] path=[\"form\"] … `label`."
× the object binding sits on the container … → expected { type: 'grid', …(5) } to not have property "object"
× the form view carries no rendered `label` … → expected { type: 'simple', …(2) } to not have property "label"
✓ the list view keeps its declared `label`      ← 合法键,正确保持绿
  Tests  5 failed | 1 passed (6)

恢复后 6/6 绿。key-vs-value 判据:本 pin 守的是是否为该形状的合法成员,但断言取 full-parse-green 而非仅 unrecognized_keys 缺席 —— 因为契约是「整个响应体满足声明」,只钉部分判据会放过其他漂移。

五、验证

结果
pnpm --filter @objectstack/metadata-protocol test 50 files / 508 tests passed
新 pin 单跑(verbose) 6/6 passed
pnpm --filter @objectstack/metadata-protocol build(含 DTS) 绿,dist/index.d.ts 197.37 KB
check:route-envelope 绿(8 route modules:7 conformant / 0 ratcheted / 1 exempt)
check:engine-double-contract 绿(75 pinned / 133 debt / 2 exempt)
check:type-check-coverage 绿(62/77 packages;metadata-protocol 属 DEBT 增长棘轮,未增长)
check:nul-bytes + 改动文件自扫 绿(5944 files;grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' 无命中)
check:empty-changeset 绿
eslint(改动文件,--no-inline-config) 绿
消费半径扫掠 runtime / rest 侧全部以 vi.fn().mockResolvedValue(...) 整体替身,钉的是 dispatcher 透传而非生产端形状 ⇒ 无 fixture 需迁移;domain-handler-registry + error-passthrough targeted 跑 62 passed

六、对在飞单的影响


Generated by Claude Code

…hema 对齐 (#5948)

GET /ui/view/:object/:type 由 getUiView 产出、REST 裸发(res.json,不套信封不校验)。
其声明是 GetUiViewResponseSchema(= ViewSchema),但实发 body 里的 list.object /
form.object / form.label 三个键,ListViewSchema / FormViewSchema 这两个 strictObject
从未声明,实测 safeParse 判 unrecognized_keys 红。因为该 schema 全仓没有运行时读者,
这处分裂此前没有任何断言看得见。

- object 上移一层到容器(ViewSchema 一直在容器层声明它),成员层那份是冗余副本;
- form.label(原 `Edit ${…}`)是渲染串而非元数据,直接摘除、不上移;
- list.label 不动 —— ListViewSchema 正式声明了它。

packages/spec 零改动:本次是把实现修正到既有声明,而非改声明迁就实现。

常驻 pin 用生产端真实组装路径(实调 getUiView)喂 safeParse,而非手拼 fixture。
反向验证:恢复任一多发键 → pin 转红并点名该键。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011M7UwH25Unfi73UHim7ajY
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 7, 2026 12:50pm

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/metadata-protocol.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata-protocol)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/metadata-protocol)
  • content/docs/releases/v9.mdx (via @objectstack/metadata-protocol)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@qq9340100
qq9340100 marked this pull request as ready for review August 7, 2026 13:12
@qq9340100
qq9340100 added this pull request to the merge queue Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

ACCEPT(spec 车道 PM 验收,session_011M7UwH25Unfi73UHim7ajY)

逐 job 结论亲读:23 checks 全 completed,21 success + 2 预期 skipped(Console Pin Gate / Build Docs),ESLint 与 TypeScript Type Check 均 success。验收面核对:

  1. 重裁选项 3 如实执行 —— 生产端(protocol.ts)摘多发键向既有声明对齐,packages/spec 零改动;object 上移容器层是 ViewSchema 容器声明的原有语义,属对齐而非偏移;
  2. pin 纪律 —— 常驻 pin 走生产端真实组装路径喂 safeParse,反向验证方向事先声明并逐字命中;
  3. 定级依据成立 —— patch 有消费面实测支撑(client-react any 透传、objectui meta.getView 零命中);cloud 仓未验面已如实标注,方向(上移一层)已写明;
  4. 在飞零相交 —— ADR-0122 第二期:major 窗口翻转裸名为解析态语义 + XInput 去留 #6083 / view 的 spec 校验闸门形同虚设:saveMetaItem({ item: { nope: 1 } }) 返回 success 并把 {"nope":1} 存成一个 active view #5599 / [spec] View-Management 五方法(listViews/getView/createView/updateView/deleteView)是零实现零路由的声明面 —— 已导致 #5948 把它的响应 schema 误当成线上路由的契约 #6239 影响分析在案,与派发时的排序约束一致。

已翻 ready + 挂 auto-merge。#6239(五休眠声明退役)按维护者裁决排 #6083 之后,由本席巡检跟进。


Generated by Claude Code

Merged via the queue into main with commit dba7747 Aug 7, 2026
24 checks passed
@qq9340100
qq9340100 deleted the claude/issue-5948-getuiview-slim-body branch August 7, 2026 13:38
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

1 participant