发现于 #5496 (time-relative 描述符 authoring 期校验)的实测。属 PD #10 的范围外发现,#5496 内刻意不修(见下「为什么不在 #5496 里修」),交 PM 定级。
事实(实测,非推断)
config.timeRelative 写成非对象标量 时,引擎解析不出任何 trigger,flow 绑不上任何东西、永不触发,而且每一层都零输出 。
复现的 flow(除描述符外一切合规:对象存在、status: 'active'、runAs: 'system'):
{
name : 'scalar_time_relative' ,
type : 'autolaunched' ,
status : 'active' ,
runAs : 'system' ,
nodes : [
{ id : 'start' , type : 'start' , config : { timeRelative : 'daily' } } ,
{ id : 'end' , type : 'end' } ,
] ,
edges : [ { id : 'e1' , source : 'start' , target : 'end' } ] ,
}
实测两条 flow lint 的输出:
validateFlowTriggerReadiness = []
lintFlowPatterns = []
原因链,逐环:
AutomationEngine.resolveTriggerBinding(packages/services/service-automation/src/engine.ts:1517)的路由判据是
config.timeRelative != null && typeof config.timeRelative === 'object' —— 标量不满足,不路由到 time_relative 。
继续下落:config.schedule != null || flow.type === 'schedule' 不成立(autolaunched、无 schedule),flow.type === 'api' || triggerType === 'api' 不成立 → 函数 return undefined(engine.ts:1554)。
activateFlowTrigger 的 if (!resolved) return;(engine.ts:1564)—— 静默返回 ,连 warn 都没有。对比:描述符是对象时至少有 TimeRelativeTrigger.start() 的 bind 期 warn。
authoring 期同样沉默:validate-flow-trigger-readiness.ts 的 isTimeRelative 也要求 typeof === 'object'(与引擎一致),所以 §1b 两条规则都不看它;更关键的是 isAutoTriggered 因此为 false,连 flow-draft-status-ambiguous 都不报。lint-flow-patterns.ts:254 的 startCfg.timeRelative != null 倒是命中,但那条规则只在 runAs 未提权时才出诊断,本例 runAs: 'system' 已合规,故也沉默。
所以标量描述符比对象描述符更静默 :后者至少有一行服务器日志 warn(这正是 #5496 修掉的 authoring 期缺口),前者连那行都没有。
这与 validate-flow-trigger-readiness.ts §1d 存在的理由(#3481 )完全同构,只是换了一个键:非字符串 triggerType 折叠成「无 trigger」→ flow 被当作 manual → 零输出。该文件的 §1d 注释原话:「a non-string triggerType folds to "no trigger"… so it binds to nothing and never fires, with zero output at any layer」。把 triggerType 换成 timeRelative、把「非字符串」换成「非对象」,就是本条。#3481 当时被判定为值得单开一条规则。
#5496 落地的规则(PR 见该单)判据刻意与引擎的路由判据逐字一致(!= null && typeof === 'object'),这样规则只为「引擎真正交给 time-relative trigger 的那些 flow」发言。要覆盖标量就得放宽 isTimeRelative,而该变量同时喂 §1b 的 unknown-object 规则和 isAutoTriggered(draft-status 规则)—— 放宽会改变另外两条已发布规则的覆盖面,超出 #5496 的范围。故单开。
建议方向(供 PM 定级,不预设结论)
在 validate-flow-trigger-readiness.ts 新增一条判据 :config.timeRelative != null && typeof !== 'object' → 报「这个槽要求描述符对象,标量不会被路由到任何 trigger,flow 永不触发」。不动 isTimeRelative,新增独立判据,避免波及另两条规则。规则 id 可复用 timeRelative 描述符跑不通时 authoring 期零诊断 —— 两条 flow lint 一条只看非空、一条只看对象名(#4966 建议 2) #5496 的族前缀风格(flow-time-relative-descriptor-invalid,或另起一条点明「不是对象」)。
同时补 isAutoTriggered :让标量 timeRelative 也算「作者意图自动触发」,这样 draft-status 规则也能覆盖。需评估对现有 flow 的诊断增量。
什么都不做 :标量拼法罕见,且 未知键静默剥离仍是全仓默认:把 #3405 的 strict 收紧从一个 schema 推广到整个可授权面(ADR-0078 完整性闸门) #4001 收紧后描述符对象内部的键已有保护。
倾向 1 的理由在「让 AI 写的元数据难以写错」这一轴上:AI 作者把 cadence 概念和描述符概念混淆时,timeRelative: 'daily' 是一个很自然的错写,而它当前的反馈是绝对静默 ——比 #5496 修掉的那个缺口还差一档。同时这不引入任何消费端宽容(PD #12 ):不接受标量、不做 coercion,只是把「引擎解析不出 trigger」这件已经确定的事提前到 authoring 期报出来。
关联:#5496 (本条的来源)、#3481 (同构先例)、#5482 (同族:节点 config 里已能判定、authoring 期无人报警)、ADR-0018(node config 开放槽)。
Blocked-by: #5496
Generated by Claude Code
发现于 #5496(time-relative 描述符 authoring 期校验)的实测。属 PD #10 的范围外发现,#5496 内刻意不修(见下「为什么不在 #5496 里修」),交 PM 定级。
事实(实测,非推断)
config.timeRelative写成非对象标量时,引擎解析不出任何 trigger,flow 绑不上任何东西、永不触发,而且每一层都零输出。复现的 flow(除描述符外一切合规:对象存在、
status: 'active'、runAs: 'system'):实测两条 flow lint 的输出:
原因链,逐环:
AutomationEngine.resolveTriggerBinding(packages/services/service-automation/src/engine.ts:1517)的路由判据是config.timeRelative != null && typeof config.timeRelative === 'object'—— 标量不满足,不路由到 time_relative。config.schedule != null || flow.type === 'schedule'不成立(autolaunched、无 schedule),flow.type === 'api' || triggerType === 'api'不成立 → 函数return undefined(engine.ts:1554)。activateFlowTrigger的if (!resolved) return;(engine.ts:1564)—— 静默返回,连 warn 都没有。对比:描述符是对象时至少有TimeRelativeTrigger.start()的 bind 期 warn。validate-flow-trigger-readiness.ts的isTimeRelative也要求typeof === 'object'(与引擎一致),所以 §1b 两条规则都不看它;更关键的是isAutoTriggered因此为 false,连flow-draft-status-ambiguous都不报。lint-flow-patterns.ts:254的startCfg.timeRelative != null倒是命中,但那条规则只在runAs未提权时才出诊断,本例runAs: 'system'已合规,故也沉默。所以标量描述符比对象描述符更静默:后者至少有一行服务器日志 warn(这正是 #5496 修掉的 authoring 期缺口),前者连那行都没有。
与 #3481 的同构
这与
validate-flow-trigger-readiness.ts§1d 存在的理由(#3481)完全同构,只是换了一个键:非字符串triggerType折叠成「无 trigger」→ flow 被当作 manual → 零输出。该文件的 §1d 注释原话:「a non-string triggerType folds to "no trigger"… so it binds to nothing and never fires, with zero output at any layer」。把triggerType换成timeRelative、把「非字符串」换成「非对象」,就是本条。#3481 当时被判定为值得单开一条规则。为什么不在 #5496 里修
#5496 落地的规则(PR 见该单)判据刻意与引擎的路由判据逐字一致(
!= null && typeof === 'object'),这样规则只为「引擎真正交给 time-relative trigger 的那些 flow」发言。要覆盖标量就得放宽isTimeRelative,而该变量同时喂 §1b 的 unknown-object 规则和isAutoTriggered(draft-status 规则)—— 放宽会改变另外两条已发布规则的覆盖面,超出 #5496 的范围。故单开。建议方向(供 PM 定级,不预设结论)
validate-flow-trigger-readiness.ts新增一条判据:config.timeRelative != null && typeof !== 'object'→ 报「这个槽要求描述符对象,标量不会被路由到任何 trigger,flow 永不触发」。不动isTimeRelative,新增独立判据,避免波及另两条规则。规则 id 可复用timeRelative描述符跑不通时 authoring 期零诊断 —— 两条 flow lint 一条只看非空、一条只看对象名(#4966 建议 2) #5496 的族前缀风格(flow-time-relative-descriptor-invalid,或另起一条点明「不是对象」)。isAutoTriggered:让标量timeRelative也算「作者意图自动触发」,这样 draft-status 规则也能覆盖。需评估对现有 flow 的诊断增量。倾向 1 的理由在「让 AI 写的元数据难以写错」这一轴上:AI 作者把 cadence 概念和描述符概念混淆时,
timeRelative: 'daily'是一个很自然的错写,而它当前的反馈是绝对静默——比 #5496 修掉的那个缺口还差一档。同时这不引入任何消费端宽容(PD #12):不接受标量、不做 coercion,只是把「引擎解析不出 trigger」这件已经确定的事提前到 authoring 期报出来。关联:#5496(本条的来源)、#3481(同构先例)、#5482(同族:节点 config 里已能判定、authoring 期无人报警)、ADR-0018(node config 开放槽)。
Blocked-by: #5496
Generated by Claude Code