Skip to content

fix(runtime): publish-drafts 中途失败时,已落盘的可见性翻转不再从响应消失 (#5242) - #5392

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-5242-packages-unhidden-apps
Aug 5, 2026
Merged

fix(runtime): publish-drafts 中途失败时,已落盘的可见性翻转不再从响应消失 (#5242)#5392
baozhoutao merged 1 commit into
mainfrom
claude/issue-5242-packages-unhidden-apps

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #5242

前提核验(先做的事)

本文件今天刚被 #5385(PR 合入 fd8521f92)重签名,issue 里的行号已漂。对 origin/main
(本次分支基点 ada0b84a3)重新核过,缺陷形状原样成立:

  • packages/runtime/src/domains/packages.ts:216 const unhidden: string[] = [] 声明在 try 之内;
  • :230 if (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)抛异常时:

  1. 前 k-1 个确实已经翻转并持久化,但 unhidden 随栈丢弃,result.unhiddenApps 压根不存在
    —— 调用方被告知"翻转失败",看不到"其中若干个已经生效"。机器可读面漏报了真实发生的事。
  2. metadata:reloaded 对这 k-1 个 app 连带漏播:announce 段读的就是这个缺失字段,
    于是它们不进 changed,boot-cached 的消费者(首当其冲是 automation engine)不重新同步,
    要等下一次重启 —— 用户可达的静默丢数据。

改法(以及为什么是这个改法)

按 PM 裁定取增量累积而非预校验,理由来自代码的实际结构:每次 saveMetaItem 都是独立、
各自落盘的写入,没有可以先行判定全体成败的联合事务。预校验(先探测哪些能写)既不可靠
(写权限/后端故障在探测与写之间会变),也仍然会在中途失败时留下同样的报告缺口。因此:

  • unhidden 与它的赋值一并提到 try 之外;名字只在对应 saveMetaItem 兑现之后才 push
    —— 所以这个列表在任意时刻恰好等于"已经落盘的那些"。
  • 赋值移到 try/catch 之后,成功与中途失败两条路径都会执行,并且仍在 announce 段之前
    (该段读这个字段,顺序是承重的,注释里钉住了)。
  • 部分失败时 unhiddenAppsunhideError 并存:前者说什么翻成功了,后者说还有没翻完的
    —— 这就是裁定要求的"名出什么翻了、什么没翻"。
  • 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:reloadedchanged 恰为
['app/alpha', 'app/beta'],以及日志两半都点到名。

✓ POST /packages/:id/publish-drafts unhides the package's hidden app
✓ POST /packages/:id/publish-drafts reports (not throws) when the visibility flip fails
✓ POST /packages/:id/publish-drafts logs at ERROR (consequence + fix) when the saveMetaItem write fails
✓ POST /packages/:id/publish-drafts reports the apps already unhidden when the flip fails MID-LOOP
 Test Files  1 passed (1)
      Tests  235 passed (235)

@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.mjs OK。

反向验证(方向先判后跑)

预判为:把实现回退成 origin/main 原样、只留新测试,已落盘的两个 app 应当从响应里消失。
实跑一致 ——

× POST /packages/:id/publish-drafts reports the apps already unhidden when the flip fails MID-LOOP
  → expected undefined to deeply equal [ 'alpha', 'beta' ]

即缺陷本身:两个真实发生的翻转在响应里变成 undefined。测试停在第一条断言上,广播与日志
两条断言在其下游,同属这一次回退所暴露的同一事实。


🤖 Generated with Claude Code

https://claude.ai/code/session_01VkPSGsX9o17MsGv3Lbxu2w


Generated by Claude Code

`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
@vercel

vercel Bot commented Aug 5, 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 5, 2026 4:24am

Request Review

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

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/runtime.

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

  • content/docs/api/client-sdk.mdx (via packages/runtime)
  • content/docs/api/index.mdx (via @objectstack/runtime)
  • content/docs/api/wire-format.mdx (via @objectstack/runtime)
  • content/docs/automation/hook-bodies.mdx (via @objectstack/runtime)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/runtime)
  • content/docs/concepts/north-star.mdx (via packages/runtime)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/runtime)
  • content/docs/deployment/index.mdx (via @objectstack/runtime)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx (via @objectstack/runtime)
  • content/docs/deployment/vercel.mdx (via @objectstack/runtime)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/runtime)
  • content/docs/kernel/cluster.mdx (via @objectstack/runtime)
  • content/docs/permissions/authentication.mdx (via @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via packages/runtime)
  • content/docs/plugins/packages.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/runtime)
  • content/docs/releases/implementation-status.mdx (via @objectstack/runtime)
  • content/docs/releases/v17.mdx (via @objectstack/runtime)

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.

@baozhoutao
baozhoutao marked this pull request as ready for review August 5, 2026 04:26
@baozhoutao
baozhoutao enabled auto-merge August 5, 2026 04:26
@baozhoutao
baozhoutao added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit ef7845a Aug 5, 2026
24 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-5242-packages-unhidden-apps branch August 5, 2026 04:41
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

Development

Successfully merging this pull request may close these issues.

publish-drafts 可见性翻转中途失败时,已翻转的 app 从响应里整批消失 —— unhiddenApps 丢了,metadata:reloaded 也漏播

2 participants