fix(metadata-protocol): read decorations stop round-tripping into persisted metadata bodies (#4326) - #4334
Merged
Merged
Conversation
…sisted metadata bodies (#4326) `getMetaItem`/`getMetaItems` decorate every served document with `_diagnostics` (and `_draft` on preview reads), while the write path persists the request body verbatim by design (ADR-0005 §Validation). Nothing stripped the decorations in between, so the standard designer round-trip — GET the served document, edit a field, PUT the whole body back — baked a stale read-time verdict into sys_metadata.metadata, its checksum, and every history diff. Never user-visible (reads recompute and the fresh verdict shadows the stale one), which is why it needed a pin on the stored bytes rather than a behaviour test: verified by disabling the strip and watching all three round-trip tests go red. `saveMetaItem` now strips the decorations before the destructive-change diff, the schema gate, the authoring gate and persistence. A silent strip, unlike the neighbouring layered-envelope rejection: these are our own decorations riding on a document that is otherwise exactly what the author edited, so rejecting the round-trip would be hostile. The ADR-0010 protection envelope (`_lock`/`_lockReason`/`_provenance`) and `_packageId` are deliberately left alone — envelope state the write path legitimately carries. Also closes out the #3903 residual verification: every `SysMetadataRepository.get` caller was audited and the verbatim boundary documented on the method — three sites read only `hash` (parent-version lineage, existence probes; converting would break the body↔hash pairing), `diffMeta` compares against equally-verbatim history rows (converting one side would render the conversion as a user edit), and the seed-publish path is the one structurally-serving caller — vacuous today since no conversion targets the seed surface, named as the seam to wire if one lands. Closes #4326. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ns-roundtrip-4326
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
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.
Closes #4326。这是 #4317(#3903)的两个收尾之一,顺带做完了当时留下的残留核查。
问题
getMetaItem/getMetaItems给每个被服务的文档盖上_diagnostics(预览读还加_draft),而写入端按设计 verbatim 持久化 request body(ADR-0005 §Validation——用parsed.data会把 Studio 专有的辅助字段剥掉)。中间没有任何一处剥离装饰,于是最标准的设计器往返:把读取时刻的过期判定写进了
sys_metadata.metadata、它的 checksum、以及此后每一条 history diff。为什么一直没人发现:读取端每次都重算,
decorateMetadataItem用{ ...item, _diagnostics }展开,新判定盖住旧的——用户永远看不到错误的诊断。坏掉的是存储的字节:一次纯装饰的重存会让内容 checksum 漂移,history diff 里出现作者从没写过的诊断噪音。也正因为不可见,这里需要的是盯住存储字节的钉子测试而不是行为测试。我用禁用修复的方式验证过:三条往返测试全部变红,恢复后全绿——测试确实咬得住。
改动
saveMetaItem在所有读取 body 的步骤之前剥离装饰(破坏性变更 diff、schema 网关、authoring 网关、持久化),新增stripReadDecorations(导出供测试)。几个刻意的设计选择:
_lock/_lockReason/_provenance)与_packageId:那些是写入端合法承载并合并的信封状态(mergeArtifactProtection),不是读取装饰。误剥会放松打包件的锁。graftNormalizedOperators的纪律)。_diagnostics其实有两个生产者:除decorateMetadataItem外,视图容器展开的命名冲突告警(stampRenameWarning,spec/ui/view.zod.ts)也盖同一个键。两者都是从文档派生的,所以都不该待在文档里——按键剥离对两者都成立。顺带:#3903 的残留核查已闭环
我在 #4317 里把
SysMetadataRepository判定为"版本层、刻意不转换",但没穷尽验证有没有服务路径绕过 protocol 的已转换缝隙。这次把get()的全部调用方过了一遍,结论写进了方法文档:saveMetaItem/revertCommit/deleteMetaItemhash(父版本谱系、存在性探测)diffMetacurrent.body与sys_metadata_history的行体对比publishPackageDrafts(seed)applySeedBodiesgetByHash无外部消费者、listDrafts不返回 body、history按设计 verbatim。边界成立。测试
新增
protocol.read-decorations.test.ts(8 条):_packageId;_diagnostics且除被编辑键外与首存逐字节相同;预览读的_draft不入库(draft 态仍在state列);纯装饰重存 checksum 不漂移。合并 main 后重跑:metadata-protocol 136、rest 523、runtime 951、plugin-security 677、metadata 281、objectql 1374,全绿。ESLint 干净(
sys-metadata-repository.ts那条rule not found在 main 上同样存在,与本改动无关,未触碰)。🤖 Generated with Claude Code