Repository navigation
Description
Found while implementing #3823 (PR #5560). Not a defect anyone hits today — filed as an observation so the ADR's "tracked separately" is true.
What
ActionDescriptor.resumeAuthority (packages/spec/src/automation/node-executor.zod.ts) defaults to 'any'. The #3801 resume gate therefore covers a pause only when its node type's author remembered the flag: a new pausing node type ships raw-resumable by omission, which is the "declared ≠ enforced" trap (Prime Directive #10) one node away.
That is not hypothetical drift — it is exactly how #3823 happened. ADR-0044 pointed the revise edge at a generic wait, wait is 'any' (correctly, for a signal wait), and the pause in the service-owned position inherited the fail-open default. The demonstrated cost there was an unaudited resubmit plus, opportunistically, remote run destruction.
ADR-0044's 2026-07-28 amendment records the long-term fix and defers it:
Fail-closed descriptor default. … The long-term-correct default is that a pause is not raw-resumable unless it opts into
'any'(screen / wait-signal declaring it explicitly). A breaking change to the descriptor default; needs a migration; tracked separately.
A search of open + closed issues for resumeAuthority returns #3823, #3853 and an unrelated typecheck issue — no issue tracks this. So the ADR points at a tracking item that does not exist, which is how a deferred decision quietly becomes a forgotten one.
Current state (why this is observation-class, not a defect)
Every pausing type in the tree today is correct on its own terms:
| type | resumeAuthority |
correct? |
|---|---|---|
screen |
'any' (default) |
yes — the UI flow-runner owns it |
wait |
'any' (default) |
yes — an external producer is meant to resume a signal wait |
approval |
'service' (explicit) |
yes (#3801) |
approval_revise |
'service' (explicit) |
yes (#3823 / PR #5560) |
subflow / map |
'any', but the gate follows the linked run to the child |
yes (#3853) |
So nothing is exploitable through the default right now. The exposure is prospective: the next pausing node type — ours or a plugin's — is fail-open unless someone remembers, and the two 'service' declarations that exist were both added after a bypass was demonstrated rather than at design time.
What a fix would involve (sketch, not a decision)
- Flip the default to
'service'(or introduce a required field / an explicit'any'opt-in) — a breaking descriptor change: every third-party pausing executor that relies on today's default would start refusing the generic resume route. - Migration surface: the ADR-0087 conversion/migration registries,
screenandwaitdeclaring'any'explicitly, the changeset FROM→TO for plugin authors, and theresume-authority-gatetests. - Worth weighing against the cheaper half-measure: keep the default and make the absence loud instead (e.g. a descriptor-registration warning when
supportsPause: trueandresumeAuthoritywas never declared), which needs no migration and no breaking change.
Refs #3801, #3823, #3853; ADR-0044 amendment ("Directions recorded but deliberately not built here"), ADR-0019's #3801 addendum.
Generated by Claude Code
Activity
os-zhuang commentedon Aug 5, 2026
发现分诊轮判级:转决策箱
needs-user-decision,域改挂domain:spec(摘finding/domain:services)。理由:本单的两个候选修法都是公开契约拍板 —— 主案「
resumeAuthority默认翻成 fail-closed」是 breaking descriptor change(第三方 pausing executor 依赖现默认者会开始拒绝 generic resume 路由,需 ADR-0087 迁移面 + changeset FROM→TO),备案「保留默认、注册时对未声明者打响警告」不破坏契约但把 fail-open 认定为长期语义 —— 二者取舍是维护者的 appetite 决定,ADR-0044 修正案自己写了 "tracked separately" 而本单正是那个缺失的跟踪项。域改标理由(按落点锚定):默认值声明在packages/spec/src/automation/node-executor.zod.ts,主案的迁移面(ADR-0087 注册表、screen/wait显式声明)也全在 spec —— 立单时的domain:services按标题词汇落了 automation,落点实为 spec 契约面。过时前提检查(origin/main @ b4cdc58):该文件自立单后零变更;今日现役 pausing 类型逐个正确(正文表),暴露面是前瞻性的,决策不急但不能再无跟踪。本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
维护者裁决(2026-08-06,经 services 车道 PM 转达):批准两步走
resumeAuthority的 pausing executor 打响亮告警 —— 告警期同时是主案的实测窗口(量到底有多少未声明者)。screen/wait的显式声明。摘
needs-user-decision回pm:queue。执行拆分:第一步落点(告警挂注册面还是 spec 校验面)核实后定车道 —— 若在service-automation注册通路则 services 车道执行,若在 spec 校验面则 spec 座位;第二步保持挂本单,待 breaking 窗口排程,spec 座位执行。Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
认领(仅第一步)(services 车道 PM 派发,
session_01BWS4heBoAitLmzCLhcYdbK)落点核实(裁决评论第 1 项):resume 闸门与描述符注册机制均在
packages/services/service-automation/src/engine.ts(resolveResumeAuthority:2575 一带、拒绝路径 :2546),第一步告警不需要动 spec(默认值与 schema 零改动)—— 按裁决的车道判定归 services 车道执行。第二步(翻默认,breaking)继续挂本单,待 breaking 窗口,spec 座位执行,本认领不含它。claude/issue-5561-resume-authority-warn../objectstack-issue-5561(dev 子代理按 worktree-first 自建)engine.ts(描述符注册路径:supportsPause: true且resumeAuthority未显式声明 → 响亮告警一次)、同包测试(含resume-authority-gate.test.ts相邻资产不回归)、.changeset/*.md。注意:Zod.default('any')会在 parse 后抹掉「未声明」这个事实,「未声明」的探测层(parse 前原始输入 / 引擎侧描述符边界)由 dev 按代码证据定;若探测必须改 spec schema 形状才能实现,停手needs_decision。Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
第一步执行受阻,决策卡修正(services 车道 PM,
session_01BWS4heBoAitLmzCLhcYdbK;dev 以 needs_decision 停手,零代码零 PR,worktree 已清)证否的是裁决形状的可实现性,不是前提:issue 半措施原文 "warning when
supportsPause: trueandresumeAuthoritywas never declared" 中的 "never declared" 在注册边界上不可表示 —— 两条实测:(1) zod 4.4.3 复刻 schema,未声明与显式'any'的 parse 产物逐字节相同(.default('any')在引擎看到对象前落定);(2)z.infer输出类型该字段必填(tsc TS2741,与 #4311 DEBT 账engine.test.ts misses resumeAuthority吻合)。任何忠实的「未声明」探针都必须先动 spec。实测人群基线(供重裁参考):全仓注册描述符 population = service-automation/builtin + plugin-approvals;pausing 共 6 个(
screen/wait/subflow/map未声明但语义正确,approval/approval_revise显式'service');示例应用零注册,objectui/cloud 对该字段零读者 —— 「第三方 pausing 插件漏声明」今天实例数为 0,已发生的一次(#3823)是仓内漏声明。四条路(dev 报告全文见其 PR-less 分析,要点):
resumeAuthority去掉.default改.optional()——undefined即未声明;运行时语义零变化(唯一消费点 engine.ts:2715 本就?? 'any');注册告警变为可实现;第二步收缩成把这个??改'service'的一行。代价:api-surface 记账 + authorable-surface/docs regen。check:*脚本 AST 扫defineActionDescriptor字面量,supportsPause: true且无resumeAuthority→ CI 红。零 spec 改动、零启动噪音、精确覆盖 automation: the revise-windowwaitpause is service-owned but type-keyed gating can't see it #3823 的真实机制(仓内漏声明 = 今天的全部人群)。PM 建议(修正案):C 立即落地 + A 一并立即落地(A 含:4 个 builtin 显式声明
'any'、注册告警按原裁决语义落地 —— 此后告警今天零点名、只捕未来遗漏;均非 breaking)。若不想现在吃 A 的 api-surface 记账,则仅 C 立即、A 与第二步同窗口。附属问题:#5703(supportsPause零运行时读者)不阻塞 C(静态门扫的是作者写下的字面量),在门/告警文本里写明该缝即可,#5703 走正常分诊。挂回
needs-user-decision,认领释放,待重裁后再派。Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
维护者裁决(2026-08-06,经 services 车道 PM 转达):批准修正案 C + A,同时立即落地(均非 breaking)
defineActionDescriptor字面量,supportsPause: true且缺resumeAuthority→ CI 红;按既有check:*家族的接线方式挂入管线。packages/spec/src/automation/node-executor.zod.ts的resumeAuthority由.default('any')改.optional()(运行时语义零变化,消费点 engine.ts?? 'any'不动);4 个 builtin(screen/wait/subflow/map)显式声明'any';注册告警按原第一步语义落地(supportsPause === true && resumeAuthority === undefined→ 每类型一次响亮 warn,显式声明即消音)—— 落地后告警今天零点名、只捕未来遗漏;api-surface / authorable-surface / docs 按 os-regen 流程 regen。??一行)。ActionDescriptor.supportsPause/isAsync是零读者的声明字段 —— 引擎判定「暂停」的唯一事实是execute()返回的suspend: true#5703 的缝(supportsPause零运行时读者)在门与告警文本/TSDoc 中写明,该单本身走正常分诊。摘
needs-user-decision回pm:queue,原认领恢复(分支claude/issue-5561-resume-authority-warn不变),文件面扩充申报:spec 一行 + regen 生成物、service-automation(engine.ts 告警 + 4 个 builtin 声明)、scripts/check-resume-authority-declared.mjs+ 管线接线、两包测试、changeset(spec + service-automation)。Generated by Claude Code
os-zhuang commentedon Aug 6, 2026
PM 复核:ACCEPT(第一步 C + A) — PR #5725(session_01BWS4heBoAitLmzCLhcYdbK 车道)
.optional()(运行时语义零变化,?? 'any'原样 — 第二步由此收缩为单一表达式);注册告警按类型去重、每实例一次(嵌入式 host 直接 new AutomationEngine() 而不调用 sealNodeTypeVocabulary(),就完全拿不到节点类型校验 —— #4771 的连带回退,靠文档而非强制 #4792 先例),只陈述静态事实(startup-registry-verdict 实测绿);四个 builtin 补显式'any'且 subflow/map 注明该值非最终权威(automation: the #3801 resume gate followssubflow:but notmap:, and the resume body can write engine-internal variables #3853)。落地后零点名,且零点名测试同时断言四个 pausing builtin 确实注册并声明 — 防空绿。merge.os-regen.driver指向「上一个装过依赖的 worktree」的绝对路径 —— 该 worktree 一删,全容器的生成物合并驱动就坏了 #4868 家族),三个刻意边界(顶层属性 / 只扫发货源码 / 夹具豁免)论证充分;管线接线照 fix(devx): check:engine-double-contract 看不见零/单形参的假引擎 delete —— 放宽判据 + 全量记债 #5694 先例。ActionDescriptor.supportsPause/isAsync是零读者的声明字段 —— 引擎判定「暂停」的唯一事实是execute()返回的suspend: true#5703 的缝写进三处(门尾注 / 告警 TSDoc / spec 字段注释),该单走正常分诊。gen:schema 重锚 baseline 的观察属check:authorable-surface在--check模式下也会重写authorable-surface.base.json—— 一次纯核验会改工作区,且任何无关 PR 都能因此静默推进删除门的锚点 #5358/os-regen 驱动指示的gen:schema在 merge 未 commit 时运行,会把 authorable-surface 锚点倒退回旧 merge-base —— 生成器写入、门全绿、静默撤销 main 的锚点推进 #5370/check:docs的第一步是gen:schema—— 修好 #4711 之后,「检查改工作区」仍从这里漏进来 #4723 已知同族,不另立单。check:docs的node-executor.mdx一行,已 --fix 入提交 — 合并后 PM 做 A-项存活核验。处置:CI 全绿后转 ready 入队。第二步(翻 fail-closed)继续挂本单待 breaking 窗口 — PR 用 Refs,本单不关。
Generated by Claude Code
feat(spec,service-automation,devx): 未声明 resumeAuthority 的 pausing 节点类…
fix(runtime): the resume door checks WHO is resuming — the run's star…
test(spec): security, ai, identity, integration, migrations, marketpl…