fix(runtime): publish-drafts 中途失败时,已落盘的可见性翻转不再从响应消失 (#5242) - #5392
Merged
Conversation
`POST /packages/:id/publish-drafts` 的 ADR-0045 可见性翻转是一个循环:每个 app 一次独立的 `saveMetaItem`,每次成功各自落盘。但 `unhidden` 声明在 try 之内、 `result.unhiddenApps` 只在整个循环跑完后才赋值 —— N 个 app 里第 k 个抛异常时, 前 k-1 个确实已翻转并持久化,却随栈一起被丢弃:响应里 `unhiddenApps` 不存在, 紧随其后读这个字段的 `metadata:reloaded` 广播也漏播这些 app,boot-cached 的 消费者(automation engine)要等下一次重启才同步。 改为增量累积:`unhidden` 与它的赋值一并提到 try 之外,名字只在对应 `saveMetaItem` 兑现之后才 push,因此该列表在任意时刻恰好等于"已经落盘的那些"; 赋值移到 try/catch 之后,成功与中途失败两条路径都执行,且仍在 announce 段之前。 部分失败时 `unhiddenApps` 与 `unhideError` 并存 —— 前者说什么翻成功了,后者说 还有没翻完的。`unhidden` 是每请求局部量,不引入共享可变状态(#5385 姿态)。 同时修正那条 error 日志的措辞:它原先断言"其 app 全部仍以 hidden: true 存着", 一旦有翻转已落盘这句话就是假的;现在按两半如实点名。 响应契约不变:仍然 200,字段仍是原来那两个,只是部分失败时可以同时出现; 重跑依旧幂等。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VkPSGsX9o17MsGv3Lbxu2w
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 21 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 5, 2026 04:26
baozhoutao
enabled auto-merge
August 5, 2026 04:26
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 #5242
前提核验(先做的事)
本文件今天刚被 #5385(PR 合入
fd8521f92)重签名,issue 里的行号已漂。对origin/main(本次分支基点
ada0b84a3)重新核过,缺陷形状原样成立:packages/runtime/src/domains/packages.ts:216const unhidden: string[] = []声明在try之内;:230if (unhidden.length > 0) (result as any).unhiddenApps = unhidden;在整个循环跑完之后才执行;:270紧随其后的重绑定段读的正是这个字段(((result as any)?.unhiddenApps ?? []))。#5385 的重接线没有移动这一段。前提有效,照 PM 裁定实现。
缺陷
ADR-0045 的可见性翻转是一个循环:每个 app 一次独立的
saveMetaItem,每次成功各自落盘。因为累积数组和结果赋值都在
try里,N 个 app 里第 k 个(k 小于 N)抛异常时:unhidden随栈丢弃,result.unhiddenApps压根不存在—— 调用方被告知"翻转失败",看不到"其中若干个已经生效"。机器可读面漏报了真实发生的事。
metadata:reloaded对这 k-1 个 app 连带漏播:announce 段读的就是这个缺失字段,于是它们不进
changed,boot-cached 的消费者(首当其冲是 automation engine)不重新同步,要等下一次重启 —— 用户可达的静默丢数据。
改法(以及为什么是这个改法)
按 PM 裁定取增量累积而非预校验,理由来自代码的实际结构:每次
saveMetaItem都是独立、各自落盘的写入,没有可以先行判定全体成败的联合事务。预校验(先探测哪些能写)既不可靠
(写权限/后端故障在探测与写之间会变),也仍然会在中途失败时留下同样的报告缺口。因此:
unhidden与它的赋值一并提到try之外;名字只在对应saveMetaItem兑现之后才 push—— 所以这个列表在任意时刻恰好等于"已经落盘的那些"。
try/catch之后,成功与中途失败两条路径都会执行,并且仍在 announce 段之前(该段读这个字段,顺序是承重的,注释里钉住了)。
unhiddenApps与unhideError并存:前者说什么翻成功了,后者说还有没翻完的—— 这就是裁定要求的"名出什么翻了、什么没翻"。
unhidden是每请求的局部量,不引入任何共享可变状态,符合 fix(runtime): 每个请求从自己解析出的 kernel 取服务 —— 多租户 host 上两个请求不再互相串改 (#5155) #5385 确立的显式传参姿态。顺带修掉那条
error日志的措辞:它原先断言"其 app 全部仍以hidden: true存着",一旦有翻转已落盘这句话就是假的,还会把运维引向"什么都没变"的错误结论。现在按两半如实点名:
哪些确实翻了(列出名字)、哪些仍然隐藏,后果与修复动作照旧。总失败一路的措辞逐字不变,
#4754 立的那条日志断言测试原样通过。
响应契约不变:仍然 200,字段还是原来那两个,只是部分失败时它们可以同时出现(此前二者
互斥纯属缺陷的副作用,没有任何 schema 或消费者声明过互斥;现存消费者读的是
?? [])。重跑依旧幂等 —— 已翻转的 app
hidden !== true,循环会跳过。测试
新增
POST /packages/:id/publish-drafts reports the apps already unhidden when the flip fails MID-LOOP:4 个隐藏 app,第 3 个(
gamma)的写入 reject。断言alpha/beta出现在unhiddenApps、unhideError与之并存、delta从未被尝试、metadata:reloaded的changed恰为['app/alpha', 'app/beta'],以及日志两半都点到名。@objectstack/runtime全包:Test Files 91 passed (91) / Tests 1337 passed (1337);pnpm --filter @objectstack/runtime typecheck无输出通过;pnpm check:durability-log-level通过(19 self-test + 24 个持久性 catch 接缝全部响亮);node scripts/check-nul-bytes.mjsOK。反向验证(方向先判后跑)
预判为红:把实现回退成
origin/main原样、只留新测试,已落盘的两个 app 应当从响应里消失。实跑一致 ——
即缺陷本身:两个真实发生的翻转在响应里变成
undefined。测试停在第一条断言上,广播与日志两条断言在其下游,同属这一次回退所暴露的同一事实。
🤖 Generated with Claude Code
https://claude.ai/code/session_01VkPSGsX9o17MsGv3Lbxu2w
Generated by Claude Code