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

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 段之前
    (该段读这个字段,顺序是承重的,注释里钉住了)。
  • 部分失败时 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'],以及日志两半都点到名。

✓ 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.

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