Blocked-by: #4771
由 PM 在复核 PR #4791(修 #4771)时提出。不阻塞那个 PR —— 它让主路径从「永远说谎」变成「永远说真话」,净收益远大于本单描述的边缘回退。
背景
#4771 把 ADR-0018 §M1 的节点类型校验从 registerFlow 挪到了 AutomationEngine.sealNodeTypeVocabulary() —— 词汇表封闭的那一刻。这是对的:插件在自己的 start() 里贡献节点类型,而 flow 在此之前就被拉起来了,所以注册时的判断是在校验一个还没成型的世界。
AutomationServicePlugin 在 kernel:bootstrapped 自动调用 seal,所以走插件的部署(绝大多数)完全没问题。
问题
直接 new AutomationEngine() 而不经过 AutomationServicePlugin 的嵌入式 host,如果不自己调用 sealNodeTypeVocabulary(),就完全拿不到节点类型校验。
对这部分 host 来说这是净回退:
也就是说这部分 host 用「不可靠的告警」换成了「没有告警」,而它们恰恰是那个告警本来可靠的人群。
为什么值得单独修
PR #4791 已经把这一点诚实地写进了 changeset 和 PR 描述,处理没有隐瞒。但**「文档写了」不等于「强制了」** —— 一个只有读过 changeset 的人才能拿到的检查,对没读的人就是静默消失的。这正是本仓一贯不接受的形态(ADR-0049、#4632、#4776 讨论的都是这一类)。
危害程度诚实评估:低。受影响的是嵌入式 host 这一小群体,且失去的是一个告警而非功能。所以这是 P3 量级,不是要紧急处理的东西 —— 但它不该只靠纪律。
一个廉价的缓解方向(供参考,不是裁定)
在 execute() 首次运行时,若 nodeTypeVocabularySealed 仍为 false,告警一次:大意是「节点类型校验从未运行 —— host 应在插件装载完成后调用 sealNodeTypeVocabulary()」。
理由:第一次真正执行 flow 的时刻,词汇表在实践上必然已经完整(不然那次执行本身就会 NO_EXECUTOR 失败),所以这个时点既安全又必然到达。它把「host 忘了调用」从静默变成响亮,而不需要引擎去猜 host 什么时候装完插件。
也可以考虑在那一刻顺带自动 seal,但那会让「谁决定词汇表封闭」有两个答案,可能不如只告警干净 —— 接手者自行判断,两种都可接受。
验收
- 嵌入式 host(直接
new AutomationEngine()、注册 flow、从不 seal)在首次执行时得到一条明确告警;
- 走
AutomationServicePlugin 的正常路径不受影响、不多打任何日志(有测试证明);
- 已经显式调用过
sealNodeTypeVocabulary() 的 host 同样不多打日志。
关联
Blocked-by: #4771由 PM 在复核 PR #4791(修 #4771)时提出。不阻塞那个 PR —— 它让主路径从「永远说谎」变成「永远说真话」,净收益远大于本单描述的边缘回退。
背景
#4771 把 ADR-0018 §M1 的节点类型校验从
registerFlow挪到了AutomationEngine.sealNodeTypeVocabulary()—— 词汇表封闭的那一刻。这是对的:插件在自己的start()里贡献节点类型,而 flow 在此之前就被拉起来了,所以注册时的判断是在校验一个还没成型的世界。AutomationServicePlugin在kernel:bootstrapped自动调用 seal,所以走插件的部署(绝大多数)完全没问题。问题
直接
new AutomationEngine()而不经过AutomationServicePlugin的嵌入式 host,如果不自己调用sealNodeTypeVocabulary(),就完全拿不到节点类型校验。对这部分 host 来说这是净回退:
registerFlow时就拿到校验。而且对「先注册执行器、再注册 flow」的 host(嵌入式场景里很常见,因为顺序完全由 host 自己控制)而言,那个校验当时是准确的 —— 它们从来没经历过 bug(service-automation): flow 节点类型校验跑在插件贡献的执行器注册之前 —— 每个 ADR-0019 approval flow 都被误报「will fail at execution time」 #4771 的误报。也就是说这部分 host 用「不可靠的告警」换成了「没有告警」,而它们恰恰是那个告警本来可靠的人群。
为什么值得单独修
PR #4791 已经把这一点诚实地写进了 changeset 和 PR 描述,处理没有隐瞒。但**「文档写了」不等于「强制了」** —— 一个只有读过 changeset 的人才能拿到的检查,对没读的人就是静默消失的。这正是本仓一贯不接受的形态(ADR-0049、#4632、#4776 讨论的都是这一类)。
危害程度诚实评估:低。受影响的是嵌入式 host 这一小群体,且失去的是一个告警而非功能。所以这是 P3 量级,不是要紧急处理的东西 —— 但它不该只靠纪律。
一个廉价的缓解方向(供参考,不是裁定)
在
execute()首次运行时,若nodeTypeVocabularySealed仍为false,告警一次:大意是「节点类型校验从未运行 —— host 应在插件装载完成后调用sealNodeTypeVocabulary()」。理由:第一次真正执行 flow 的时刻,词汇表在实践上必然已经完整(不然那次执行本身就会
NO_EXECUTOR失败),所以这个时点既安全又必然到达。它把「host 忘了调用」从静默变成响亮,而不需要引擎去猜 host 什么时候装完插件。也可以考虑在那一刻顺带自动 seal,但那会让「谁决定词汇表封闭」有两个答案,可能不如只告警干净 —— 接手者自行判断,两种都可接受。
验收
new AutomationEngine()、注册 flow、从不 seal)在首次执行时得到一条明确告警;AutomationServicePlugin的正常路径不受影响、不多打任何日志(有测试证明);sealNodeTypeVocabulary()的 host 同样不多打日志。关联