fix(metadata-protocol): boot 重水合按行的真实 package_id 登记对象归属 (#4636 裁 B 收官) - #6261
Merged
Conversation
`loadMetaFromDb` 的 object 分支从 `engine.find` 的行上读 `record.packageId`, 而 `sys_metadata` 的列是 snake_case 的 `package_id`,该表达式恒为 `undefined || 'sys_metadata'` —— 每次重启都把绑定了包的对象 overlay 登记在 哨兵下。改读 `package_id`,与写路径(#4636 PR1)、`getMetaItems` 以及相邻的 非 object 分支一致。 归属键同时是包过滤键(`getAllObjects(packageId)`),所以此前对象在创建时 出现在自己所属包的侧边栏过滤里、重启后消失;更要紧的是重启后的第一次编辑: boot 登记 `'sys_metadata'`、保存登记 `app.<slug>`,`registerObject` 抛 `already owned by package …` 被 `applyObjectRegistryMutation` 吞成 warn, 保存回 `success: true` 而内存 schema 停在重启时的版本,该笔编辑被静默丢弃 (cloud#970 的重启面)。 同时把 `objectql/src/registry.ts` `isTenantAuthored` 的契约注释收尾:摘掉 PR1 加的「这半句描述的是契约,还不是代码」标注 —— 两侧现已一致。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019Q7oc7ASjh8yxyS3Yz78We
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 15 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
baozhoutao
marked this pull request as ready for review
August 7, 2026 12:47
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4636 — 维护者 2026-08-07 裁决 B 的第二步:boot 半边,本单收官。PR1(#6219,已合入
c9bf94009)已把写路径切到真实 package id 并加上服务端强制盖章;本 PR 落地剩下的一行,两侧从此说同一句话。问题
loadMetaFromDb的 object 分支从engine.find('sys_metadata', …)返回的行上读record.packageId。但这些行是数据库行,列名是 snake_case 的package_id:sys_metadata对象声明package_id,SysMetadataRepository写package_id,getMetaItems读r.package_id,紧邻的非 object 分支三行之下就写着(record as …).package_id。只有这一支拼成了 camelCase,于是表达式恒为undefined || 'sys_metadata'—— 无论行有没有绑定包,每次 boot 都登记在哨兵下。为什么这不只是个分类瑕疵
归属键就是包过滤键(
getAllObjects(packageId)匹配contributor.packageId,runtimemeta.ts的侧边栏直接消费)。PR1 之后写路径记的是app.加真实 slug,所以残留缺陷正好是重启形状的:对象创建时在自己所属包的过滤里,重启之后就不见了。更要紧的是重启后的第一次编辑。两侧键不一致时,这次保存会以另一个键重新认领归属,
registerObject抛already owned by package "sys_metadata",而applyObjectRegistryMutation把它 catch 成console.warn:保存返回success: true、行也确实落盘,但内存 schema 停在 boot 时的版本 —— 这一笔编辑被静默丢弃,新字段在下一次重启前都不可用。这是 cloud#970 在 PR1 之后的形态:不是
403(两侧都盖_provenance: 'org',overlay 门是开的),而是一次被吞掉的 ownership 冲突。所以 pin 测试断言的不是「保存成功」,而是演进后的字段真的进了 schema。改了什么
packages/metadata-protocol/src/protocol.ts—— 一行读法:record.packageId→(record as { package_id?: string | null }).package_id。用||而非??,与写路径的request.packageId || 'sys_metadata'对称:空绑定就是「没有包」,哨兵只标记这一件事。该分支已经在盖_provenance: 'org',本 PR 不动盖章。packages/objectql/src/registry.ts——isTenantAuthored契约注释终版:摘掉 PR1 加的「这半句描述的是契约,还不是代码」标注,改成「两侧都按真实 package id 登记」。注释的结论(_packageId !== 'sys_metadata'无法回答 provenance)不变。前提复核(在
origin/main上)c9bf94009 fix(metadata-protocol): 对象 overlay 写路径的 ownership 键切真实 package id …(#6219)record.packageId || 'sys_metadata'protocol.ts:10765(立单时 ~8958,派发词记 :10657,已再漂移;按内容定位)protocol-writepath-object-ownership.test.ts:365[tripwire] a package-bound rollback still 409s,本 PR 一字未动测试
新增
packages/objectql/src/protocol-boot-object-package-binding.test.ts(5 例)。放在 objectql 而非被测代码旁,理由与 PR1 同:断言对象是真SchemaRegistry,而 objectql 依赖 metadata-protocol,只有这个方向能同时握住两半。用例不手搓行,而是跑一遍真实写路径持久化、再把落盘的行交给一个全新的 registry + protocol —— 列名拼写正是本 PR 的全部主题,手搓 fixture 会让它与仓库实际写出的形状脱钩。
package_id(且没有packageId)→ boot 后归属是app.myapp;_provenance仍是'org'(本 PR 不改盖章,显式钉住);getAllObjects('app.myapp')重新看得见,且不串到app.otherapp;due_date真的进内存 schema、归属仍是app.myapp、落盘仍是一行且仍绑在包上;_provenance: 'org',不混进任何包的过滤(回归)。packages/objectqlpnpm typecheck(tsc --noEmit)通过、零输出。metadata-protocol没有typecheck脚本(在 type-check-coverage 的 DEBT ledger 里),与 PR1 相同。门:
反向验证(方向先写死,再实测)
dist/,不是 src。下列结果都是改完重建 dist 之后的。肢 A —— 还原那一行读法(回到
record.packageId)。 预测写在跑之前,逐条:expected 'sys_metadata' to be 'app.myapp'_provenance: 'org'expected [] to deeply equal [ 'myapp_invoice' ]success上 ——saved.success仍为true(不会 403:两侧都盖'org',门是开的),红在due_date断言,因为registerObject抛already owned被吞成 warndue_date:expected [ 'organization_id', …(8) ] to include 'due_date';success断言先行通过;stderr 印出[Protocol] registerObject failed for myapp_invoice: Object "myapp_invoice" is already owned by package "sys_metadata". Package "app.myapp" cannot claim ownership.Tests 3 failed | 2 passed (5)—— 逐例符合预测,范围与方向都没有偏差。特别记一句:这一支没有 403,和裁决前 dev 实测的「boot 单独修」形态不同,正是因为 PR1 已经把盖章补上;红的是归属、包过滤,以及被console.warn吞掉的那笔编辑。必答项
#5079 终版定价 —— 成因分析不需要更新,
removeRuntimeShadow与本 PR 的结构不相交。 PR1 已实测的机制在 boot 侧原样成立:removeRuntimeShadow扫的是this.metadata.get(type)里的复合键pkg:name兄弟项,判据it._packageId !== 'sys_metadata'也是对那个 Map 求值;而 object 这一片永远只有裸键 —— boot 的 object 分支和applyObjectRegistryMutation一样只调registerObject(动的是objectContributors),plugin.ts对type === 'object'显式return(注释:objects are registered differently (ownership model)),全仓没有任何路径以 packageId 调registerItem('object', …)。所以复合兄弟项从不存在,循环走不到那个判据。本 PR 搬的仍是objectContributors的键,是removeRuntimeShadow从不触碰的另一个结构。#5079 的成因分析(「object 分支本就无 shadow 可摘」)在 #4636 完整落地后依然成立,无需重写,也不应再被定价成「等 #4636」。#6215 —— 零依赖确认。 本 PR 只动
loadMetaFromDb的一行读法与一段注释,sys-metadata-repository.ts一字未动,rollbackMetaItem/restoreVersion一字未动。#6215 的成因(restoreVersion调put不传packageId,put用whereFor(ref, state, opts.packageId ?? null),null是谓词而非「任意包」)与 boot 读法无关;两个方向都不阻塞。PR1 tripwire —— 原样未动确认。
protocol-writepath-object-ownership.test.ts整个文件本 PR 未修改(git diff不含该文件),[tripwire] a package-bound rollback still 409s仍在 :365,仍绿。#6215 修好那天它照常翻红,把接手的人指向上一条用例里已经写好的 ownership 断言 —— 这条链路完好。Generated by Claude Code