Skip to content

feat(spec,service-automation,devx): 未声明 resumeAuthority 的 pausing 节点类型不再静默 fail-open (#5561) - #5725

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5561-resume-authority-warn
Aug 6, 2026
Merged

feat(spec,service-automation,devx): 未声明 resumeAuthority 的 pausing 节点类型不再静默 fail-open (#5561)#5725
os-zhuang merged 1 commit into
mainfrom
claude/issue-5561-resume-authority-warn

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Refs #5561

维护者 2026-08-06 裁决的第一步(修正案 C + A,均非 breaking)。第二步(缺失即 fail-closed)仍挂 #5561 等 breaking 窗口,本 PR 不做。

前提重验(origin/main @ 6ddb2ec)

  • ActionDescriptor.resumeAuthority 确实 .default('any')(packages/spec/src/automation/node-executor.zod.ts);issue 作者记忆中的 supportsPause 字段真实存在(同 schema,z.boolean().default(false)),不是误记。
  • 消费侧唯一读者是 resolveResumeAuthority(engine.ts,行号已从立单时的 :2575 漂到 :2766),其 descriptor?.resumeAuthority ?? 'any' 是本 PR 刻意不动的那一行。
  • 探测层为何必须动 spec(第一轮 needs_decision 的证据,复述以便审阅):.default('any') 让「未声明」在注册边界上不可表示 —— 运行时侧,未声明与显式 'any'defineActionDescriptor 后逐字节相同(zod 4.4.3 实测 distinguishable? false);类型侧,z.infer 让该字段在输出类型里必填(tsc TS2741),所以连「运行时 undefined」都不是正当信号。四条候选路里 A 是唯一干净且非 breaking 的,C 与之互补且零 spec 依赖。

A —— spec 一行 + 声明补齐 + 注册告警

  1. resumeAuthority.default('any').optional()运行时语义零变化:resolveResumeAuthority?? 'any' 本来就在做默认值的事,未声明者今天依旧可被通用 resume 路由继续,只是不再无声。第二步因此收缩成这一个表达式,不再需要 schema 迁移。
  2. registerNodeExecutor 新增 warnIfResumeAuthorityUndeclared:supportsPause === trueresumeAuthority === undefined → 按类型去重、每 engine 实例一次的响亮 warn(去重字段沿用同包 嵌入式 host 直接 new AutomationEngine() 而不调用 sealNodeTypeVocabulary(),就完全拿不到节点类型校验 —— #4771 的连带回退,靠文档而非强制 #4792 nodeTypeSealOmissionWarned 的 per-instance 先例)。文本单行自足、写明两个合法值,并明确「显式声明 'any' 即可消音且不改变任何行为」—— 否则会把本就该开放的节点逼向 'service'。告警只陈述该描述符缺了声明这个作者时刻就固定的静态事实,不读任何注册表、不对「本次启动会不会有人后来注册」下判词,所以 check:startup-registry-verdict 绿(实测),与 嵌入式 host 直接 new AutomationEngine() 而不调用 sealNodeTypeVocabulary(),就完全拿不到节点类型校验 —— #4771 的连带回退,靠文档而非强制 #4792 同款约束。
  3. 四个 builtin 补显式声明,各自理由写在代码注释里:screen / wait(通用路由本就是它们的正门)、subflow / map(闸门会顺 subflow: / map: 关联走到子 run 判词,automation: the #3801 resume gate follows subflow: but not map:, and the resume body can write engine-internal variables #3853,所以本节点这个 'any' 不是最终生效的权威 —— 显式写出来才能让「告警在这里沉默」是决定而非缺口)。

告警今天会点名的现役类型清单

类型 位置 声明 告警
screen builtin/screen-nodes.ts 本 PR 补 'any' 不点名
wait builtin/wait-node.ts 本 PR 补 'any' 不点名
subflow builtin/subflow-node.ts 本 PR 补 'any' 不点名
map builtin/map-node.ts 本 PR 补 'any' 不点名
approval plugin-approvals/src/approval-node.ts 原有 'service' 不点名
approval_revise plugin-approvals/src/approval-revise-node.ts 原有 'service' 不点名

落地后零点名,只捕未来遗漏。这个零由一条测试钉住,且同一条测试同时断言「四个 pausing builtin 确实被注册且都声明了 'any'」—— 否则零点名可能是「什么都没注册」的空绿。

C —— 仓内静态门 check:resume-authority-declared

AST(TypeScript compiler API,与 check-engine-double-contract.mjs 同路数)扫 defineActionDescriptor({...}) 字面量:supportsPause: true 且缺 resumeAuthority → CI 红,指名文件、行号、类型与两种修法。为什么与运行时告警并存:告警说给读服务器日志的人听(第三方插件的正确频道,而本仓今天一个都没有),而 #3823 是我们自己仓内的漏声明,该告诉作者的时刻是 PR

三个刻意的边界(都写进脚本头与 --self-test):

  • 只读顶层属性screen 的描述符嵌着上百行 configSchema JSON Schema,其 properties 里可以出现任何同名键 —— 那是作者数据,不是能力声明。正则无法区分,所以读结构。自测两侧都钉了。
  • 只扫发货源码,测试夹具在范围外。仓内今天有三个未声明的 pausing 夹具,全部是刻意的 —— 尤其 resume-authority-declaration.test.ts 必须构造未声明的描述符才能测告警本身;把夹具纳入门会让被测机制无法被测。(与只扫测试的 check-engine-double-contract.mjs 正好互为镜像:每个门的范围是它的主体,不是全仓扫射。)
  • DISCOVERED 不变量:扫到 0 个描述符判为扫描坏了而非仓库干净(merge.os-regen.driver 指向「上一个装过依赖的 worktree」的绝对路径 —— 该 worktree 一删,全容器的生成物合并驱动就坏了 #4868 家族)。

管线接线照最近先例(#5694):package.jsoncheck:resume-authority-declared 先跑 --self-test 再跑扫描,lint.yml 的 lint job 里紧随 Engine test-double contract gate

regen 生成物 + 契约面存活核验

  • pnpm --filter @objectstack/spec check:generated 十项全绿,唯一实际 stale 的是 check:docs,--fix 只重生成了它:content/docs/references/automation/node-executor.mdx 一行(resumeAuthorityoptional + 新描述)。
  • check:api-surfacecheck:authorable-surface真实重建 dist 之后依旧绿 —— 实测结论:api-surface.json 记录导出符号,不记录 schema 成员的可选性(dist/node-executor.zod-*.d.ts 已确实变成 z.ZodOptional),authorable-surface.json 记录键路径而键仍在。我在决策卡里预估的 api-surface 记账成本实测为零,如实更正。
  • 序列化面:RuntimeProtocolSchema.actions 对未声明者不再带该键。三仓实测零读者(framework 内唯一读者是 resolveResumeAuthority;objectui 与 cloud 对 resumeAuthority / supportsPause / isAsync 全仓零命中)。
  • 顺带说明:任何 spec build / check 都会让 gen:schemaauthorable-surface.base.jsonbaseRev 重锚到当前 merge base 并带进别人已 merge 的键。那不是本 PR 的改动,已 revert;还原后 check:generated 仍全绿。

#5703 的缝(已在三处写明)

supportsPause 自身是运行时零强制的声明:暂停的事实来自 execute() 返回 suspend: true(engine.tsif (result.suspend)),#3801 闸门也只读 resumeAuthority。所以一个 execute() 会暂停、却把 supportsPause 留在默认 false 的执行器,告警与静态门都看不见,而且照样 fail-open。这一点写进了:门的报错尾注、warnIfResumeAuthorityUndeclared 的 TSDoc、spec 里 supportsPause 的字段注释。#5703 走正常分诊。

类型债台账的一处如实更正

scripts/check-type-check-coverage.mjs@objectstack/service-automation 的 DEBT 注记原文是「TS2741: engine.test.ts misses resumeAuthority」。.optional() 之后实测那两处 TS2741 没有消失而是转移了:同两行字面量同时也漏 handlerContract,TS 一次只报一个,于是现在报 handlerContract计数 2 不变,我只更正了归因(这句话是被本 PR 改坏的,所以归我修)。另有一处独立事实:该包实测 raw 总数为 5(多出 3 处 nested-region-parity.test.ts 的 TS2341,与本 PR 无关、main 上即如此),而台账冻结值是 2 —— 该门不重跑 tsc 所以不红;我没有动那个数字(抬高冻结值会把别人的债洗成基线),另行记到 #4311

验证

  • @objectstack/spec:317 files / 8083 tests 全绿(含新增 2 条 + 替换 1 条 resumeAuthority 用例)。
  • @objectstack/service-automation:62 files / 741 tests 全绿(新增 resume-authority-declaration.test.ts 11 条;resume-authority-gate.test.ts 19 条零回归)。
  • @objectstack/plugin-approvals(消费半径):19 files / 446 tests 全绿。
  • typecheck:spec(含 check:test-typecheck,债账 79 files / 691 errors 不变)、plugin-approvalsruntime 三包全绿。
  • 门:check:resume-authority-declared(自测 + 扫描 6/6)、check:startup-registry-verdictcheck:adr-anchorscheck:nul-bytescheck:durability-log-levelcheck:type-check-coverage 全绿;改动文件 eslint 干净;控制字符自查(grep -naP 覆盖 gate 盲区)零命中。

反向验证(先预测方向,后实测)

  1. 删掉一个 builtin 声明 → 门转红:预测红并指名该类型。实测 wait-node.ts:157 被点名、exit 1、计数 6→5。已还原。
  2. 还原 spec 的 .default('any') → 告警不可达:预测「计数类用例转红、沉默类用例保持绿但是空绿」。实测正是如此 —— 11 条里 5 条红(全部是数告警条数的),6 条绿;其中「零点名」那条也保持绿,因为四个 builtin 已显式声明,所以它钉的是零点名这个事实,机制由那 5 条钉。spec 侧 2 条(toBeUndefined / Object.hasOwn 可区分性)同时转红。已还原并重建 dist 复绿。
  3. C 对 A 的独立性:同一次还原下 check:resume-authority-declared 保持绿,如预测 —— 它读源码字面量,不读 schema 默认值。这也正是两者并存的意义。

Generated by Claude Code

…型不再静默 fail-open (#5561)

#3801 的 resume 闸门按「暂停在哪个节点」判权,所以它只覆盖作者记得声明
resumeAuthority 的 pausing 类型。而 `.default('any')` 让这个遗漏根本无法被观测:
Zod 在 defineActionDescriptor 里就把键填上,「作者选了 'any'」与「作者从未想过」
产出逐字节相同的描述符。#3823 就是这样发生的 —— ADR-0044 把 revise 边指向通用
`wait`,`wait` 的 'any' 本身正确,而站在 service 所有位置上的那次暂停继承了一个
没人选过的 fail-open 值。

本次落地维护者裁决的第一步(两处探测 + 声明补齐,均非 breaking):

- spec: resumeAuthority 去掉 .default('any') 改 .optional() —— 缺失即缺失。
  运行时语义零变化,engine 的 `?? 'any'` 原样保留(它本来就在做默认值的事),
  所以未声明者今天仍然可被通用路由 resume,只是不再无声。
- service-automation: registerNodeExecutor 对 supportsPause 且未声明
  resumeAuthority 的描述符按类型去重打一次响亮 warn,文本写明两个合法值、
  并说明显式声明 'any' 即可消音且不改变任何行为(避免把本就开放的节点逼向
  'service')。screen / wait / subflow / map 四个 builtin 补上显式
  resumeAuthority: 'any' —— 它们各自语义本就正确,只是此前在继承而非声明,
  所以告警今天零点名,只捕未来的遗漏。
- devx: 新增 check:resume-authority-declared 静态门(AST 扫
  defineActionDescriptor 字面量),仓内 pausing 描述符漏声明直接 CI 红。
  #3823 是我们自己仓内的漏声明,PR 时刻才是该告诉作者的时刻;只扫发货源码,
  测试夹具故意保留未声明形状以便测告警本身。

第二步(缺失即 fail-closed)仍挂 #5561 等 breaking 窗口 —— 经此改动后它收缩成
resolveResumeAuthority 里那一个表达式,而不再需要 schema 迁移。

supportsPause 自身是运行时零强制的声明(#5703):暂停的事实来自 execute() 返回
suspend: true。声明它的执行器漏声明 supportsPause 时,告警与静态门都看不见 ——
这一点在门的报错文本、告警的 TSDoc 与 spec 字段注释里都写明了。

Refs #5561, #3801, #3823, #3853, #5703; ADR-0044 修正案。

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BWS4heBoAitLmzCLhcYdbK
@vercel

vercel Bot commented Aug 6, 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 6, 2026 2:08am

Request Review

@github-actions github-actions Bot added the size/l label Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/service-automation, @objectstack/spec.

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

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

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.

@github-actions github-actions Bot added documentation Improvements or additions to documentation ci/cd dependencies Pull requests that update a dependency file tests tooling labels Aug 6, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 6, 2026 02:20
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit b508244 Aug 6, 2026
26 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5561-resume-authority-warn branch August 6, 2026 02:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants