Uh oh!
There was an error while loading. Please reload this page.
fix(data-objectstack): layered 读迁到声明路径 GET /meta/:type/:name/layers (#4016) - #4988
Conversation
`packages/cli` 的 `sources every range from this repo instead of inventing one` 与 `packages/create-plugin` 的 `sources every devDependency range from this repo instead of inventing them` 都在 `for` 循环里逐名 `expect`。`expect` 失败即抛,所以一次只报第一个 不匹配的名字,而表是插入序遍历的 —— 被报的是「谁排在前面」,不是「谁更严重」。 代价兑现过两次:#4098 有五个同窗 bump 藏在第一个后面;#4968 里 `lucide-react` 排在 `postcss`/`react`/`tailwindcss`/`typescript`/`vite` 之前,遮住后面六个名字的判定, 只能另写脚本才敢说这批到底漂了几处。跨包那半更贵:create-plugin 那处当时在 `main` 上也是红的,但按包边界扫 cli 时没有任何东西报它,落地后仍要第二轮。 两处都改成两趟: - 前置断言(锚必须能解析:名字至少被一个生成器声明、`root` 锚在根清单里、仓内区间 必须一致)保持首败即抛,并且整趟跑在任何区间比对之前 —— 前置败时不产出任何漂移行, 不与漂移串扰。锚不可解析时也没有可累积的期望值。 - 漂移断言累积后一次报空集,逐条带生成器、名字、实得区间与应得区间。 顺带把两张锚表的文档块补上「范围规则只判本表内的名字、完备性规则要求生成清单的键集 恰好等于本表」这一对互补关系 —— 两条规则分居两个 `it`,读任一处都看不到另一处。 Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
commented
Aug 17, 2026
PM 验收 ✅ ACCEPT(#4016,批次 20) 实物核验(merge-base skills/ 触碰的定性(写明留否决窗): CI 亲读:20/20 check runs completed,零失败(两项 path-filter skipped 计绿)。 GA 客户端问题的回答:实测三点闭合 —— spec 台账声明该路由、 反向验证读数:①ratchet「多一条发现」方向命中(非空绿);②行为钉 3 红 4 绿按预判分池(4 绿量「回来的东西没变」);③fieldEnvelope 替身预判错、如实报告 —— 替身 layered 分支对断言零压力 + findings:#4982(overlayScope 词表漂移 + LayeredDiff 裸英文徽标,今天用户可见)已由 PM 定级 Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
yinlianghui
commented
Aug 17, 2026
成因与实测记在 #4994:并行会话的 scratchpad 目录事实上是共享的,通用文件名( ⛔ 没有改写 Generated by Claude Code |
Fixes#4016
os#5882 B 案的消费半边:Studio 元数据编辑页的三层对比读,从「给普通条目读挂一个查询标记」迁到声明路径
GET /meta/:type/:name/layers。服务端半边是 os#6596,已随@objectstack/spec@17.0.0GA 落到本仓 pin(dependabot7484f7f0a/ PR #4953)。一、前提测量(动工前先量,三项)
1. GA 契约在位。 本仓解析到的
@objectstack/spec@17.0.0里,DEFAULT_METADATA_ROUTES声明了GET /:type/:name/layers(prefix/api/v1/meta,handlergetMetaItemLayered,responseSchemaGetMetaItemLayeredResponseSchema),且该 schema 确实从@objectstack/spec/api导出(运行时typeof为 function)。spec 自己的 route 描述也写明老拼法「still works during its deprecation window but is answered withDeprecation/Linkheaders pointing here」。2. GA 客户端没有 layered 方法 —— 但这不是「缺口」。
@objectstack/client@17.0.0整包 greplayered零命中,两种拼法都没有。frameworkorigin/main(e4e5c6e3c)的packages/rest/src/rest-route-ledger.ts把该路由记作disposition: 'server-only',note 原文:「consumed by objectui over plain HTTP, and the SDK expressed no layered read under the?layers=spelling either」,并明确把「SDK 是否应当表达它」留作独立的产品决定。所以本仓继续手工构造该请求,是台账声明的消费方式,不是绕过 SDK;本 PR 只搬路径,不新增任何手拼 URL(那一处手拼在改动前就已经存在)。3. 调用点清单。 构造该查询串的地方全仓只有一处:
packages/data-objectstack/src/metadata-client.ts的MetadataClient.layered()。app-shell 共 15 个client.layered()调用点全部经由它,零个自己拼 URL ——studio-design/6 处(PackageOwdOverviewPanel2、StudioDesignSurface4),metadata-admin/9 处(ResourceEditPage5、PermissionMatrixEditor3、EmbeddedItemEditor1)。除此之外还有:1 个按老 URL 匹配的 fetch 测试替身、3 处注释、1 处技能指南正文提到老拼法。前提成立,
premise_still_valid: true。二、改了什么
MetadataClient.layered()改读…/:type/:name/layers。&挂在标记后面。留着&会把它焊到最后一个 PATH 段上(…/layers&package=crm),不匹配任何路由 —— 404,而layered()把 404 报成「空但成功」的层视图,静默。已改成?package=并单独钉了一条。packages/*/src、apps/*/src与skills/**/*.md,阻止任何出货源文件或技能指南再用查询标记去取该投影。信封不变(行为等价):
code/overlay/overlayScope/effective、载入期_diagnostics、ADR-0010 完整保护信封仍在同一次往返里回来,?package=(ADR-0048)照旧透传 —— 上游两个入口由同一个 handler 服务,正是为了让弃用窗口的承诺成立。一条行为差异,是服务端的设计而不是本仓的选择:被弃用的标记在后端 protocol 实现缺
getMetaItemLayered时会下坠到普通条目读、答{ type, name, item }信封;专用路径拒绝用本路径声明的形状去答另一个资源,改答 501NOT_IMPLEMENTED(rest-server.ts 该挂载点注释原话:「A dedicated path cannot fall through to the plain read the way the flag did」)。于是这里表现为抛错,而不是code/overlay无故空白的对比视图。这条已单独钉住。新测试的路径期望派生自装到本地的
@objectstack/spec路由表,而不是抄一份字符串 —— 抄的那份在生产者搬路由之后仍然会绿,而那正是这张卡要消除的失效模式。信封等价那条先用生产者自己的GetMetaItemLayeredResponseSchema校验 fixture(断的是值的判定,所以要求 full parse 绿,不是只看没有unrecognized_keys),再断言解析结果逐键相等。三、反向验证(先预判,后跑;
git checkout还原,未用 stash)预判(跑之前写下):把
layered()的 URL 改回老拼法(含&package=)后 ——[]变成 1 个文件),不是「什么都没产出」的空绿;metadata-client.layeredRoute.test.ts7 条里 3 红 4 绿:红的是 3 条 URL 钉,绿的 4 条(spec 声明、信封等价、404、501)必须保持绿 —— 它们量的是「回来的东西没变」,而本 PR 改的是「去哪里取」;它们若也红,说明我顺手改了行为;PermissionMatrixEditor.fieldEnvelope.test.tsx红(替身按新路径精确匹配,老拼法会掉到 catch-all)。实测:1 与 2 与预判逐条一致 ——
第 3 条预判错了,如实报告:
PermissionMatrixEditor.fieldEnvelope.test.tsx三条全绿。原因是那个替身的 layered 分支对该文件的断言没有任何压力:layered body 只喂「是否 artifact-backed」的判定与 permission 草稿,而这三条断言的字段子表是由下面那条get()读喂的;掉到 catch-all 的{ items: [] }也不影响它们(client.layered(...).catch(() => null)是PermissionMatrixEditor.tsx:433有意的 fail-open,注释已论证过「a transient read failure must not invent a lock」)。我原本在那处替身上写了一句「老拼法会掉到 catch-all 并把断言一起带红」的注释 —— 实测证明那是假的,已把注释改成实测结论,并注明 URL 的钉在 data-objectstack 那份测试里。替身仍改到新路径:留着老拼法会让这个「用真 client 答真服务端形状」的替身对着线上撒谎,下一个读者会从这里抄走已弃用的拼法。
四、测试
仓根跑法(AGENTS.md §9 唯一正确写法),全部在共享验证锁内串行:
pnpm exec vitest run packages/data-objectstack/ scripts/__tests__/layered-read-declared-path-4016.test.ts packages/app-shell/src/views/metadata-admin/→Test Files 213 passed (213)/Tests 2267 passed | 1 skipped (2268)turbo run type-check --filter=@object-ui/data-objectstack --filter=@object-ui/app-shell→Tasks: 31 successful, 31 total(首轮曾红:ratchet 用 node 内建但 data-objectstack 的 tsconfig 不带 node types —— 这也是把 ratchet 放进scripts/__tests__/的原因,那里types: ["node"]且由pnpm type-check:scripts与 ci.yml 覆盖)pnpm type-check:scripts→ exit 0check:control-bytes/check:changeset-presence/check:changeset-no-major/check:spec-symbols/check:phantom-deps/check:self-import/check:skills-paths/check:type-check-coverage/check:lint-coverage→ 全绿eslint→ 0 error(只剩本文件既有的no-explicit-anywarning,均在未改动行)五、相邻发现(未在本 PR 修)
MetadataLayered.overlayScope类型是string、注释声称organization | environment | package | null,而 GA spec 与 framework 生产者都只有org/env/ null;LayeredDiff.tsx把该原值直接当徽标渲染,同行另外三个层标签都过translateConsoleValue—— zh-CN 下「有 overlay」的条目显示裸英文org/env,「没有 overlay」的回退支才是翻译过的。finding,观察类):layered()里那段「非 layered 信封」兜底在迁到新路径后已不可达(唯一生产者是老拼法的下坠,新路径答 501)。故意留在本 PR 之外 —— 删它是对「不合规后端」的行为变更,不属于搬路径这件事;等上游关掉弃用窗口后一并删。Generated by Claude Code