AutomationEngine.resolveTriggerBinding (packages/services/service-automation/src/engine.ts) keeps a private copy of the chain that decides which trigger kind a flow asks for — string record-* token, array-form token, timeRelative object, schedule cadence or type: 'schedule', type: 'api' or triggerType: 'api', in that precedence. Since #14153, @objectstack/spec exports the authoring-time mirror of that chain, resolveFlowTriggerKind (packages/spec/src/automation/flow-trigger-kind.ts), and both authoring surfaces read it: defineStack's trigger-capability refusal and @objectstack/lint's validate-flow-trigger-readiness (its auto-triggered predicate).
The engine does not. Its chain and the spec resolver are two hand-kept copies of one rule, and nothing pins them to each other: a future branch added to the engine (a new trigger kind, a widened startsWith grammar) leaves defineStack accepting a stack the runtime would leave inert, or refusing one it would arm — the exact drift the shared resolver was hoisted to prevent, reopened one layer down.
Observation, not a defect today — the two agree on every string-form flow (pinned in flow-trigger-kind.test.ts); the one deliberate divergence, the array-form triggerType, is documented in the resolver's header (the engine routes it to the record-change trigger only so that trigger can refuse it loudly at bind time; lint 1d reports the shape itself).
Suggested shape: have resolveTriggerBinding take its triggerType from resolveFlowTriggerKind(flow) and keep only the binding construction per kind (the array-form diagnostic route can stay as an explicit pre-check before the resolver). getTriggerBindingAudit and the boot banner then name the same kind defineStack named at authoring, by construction.
Blocked-by: #14153 (the spec export lands there)
Found while implementing #14153 (the defineStack trigger-capability validator).
Generated by Claude Code
AutomationEngine.resolveTriggerBinding(packages/services/service-automation/src/engine.ts) keeps a private copy of the chain that decides which trigger kind a flow asks for — stringrecord-*token, array-form token,timeRelativeobject,schedulecadence ortype: 'schedule',type: 'api'ortriggerType: 'api', in that precedence. Since #14153,@objectstack/specexports the authoring-time mirror of that chain,resolveFlowTriggerKind(packages/spec/src/automation/flow-trigger-kind.ts), and both authoring surfaces read it:defineStack's trigger-capability refusal and@objectstack/lint'svalidate-flow-trigger-readiness(its auto-triggered predicate).The engine does not. Its chain and the spec resolver are two hand-kept copies of one rule, and nothing pins them to each other: a future branch added to the engine (a new trigger kind, a widened
startsWithgrammar) leavesdefineStackaccepting a stack the runtime would leave inert, or refusing one it would arm — the exact drift the shared resolver was hoisted to prevent, reopened one layer down.Observation, not a defect today — the two agree on every string-form flow (pinned in
flow-trigger-kind.test.ts); the one deliberate divergence, the array-formtriggerType, is documented in the resolver's header (the engine routes it to the record-change trigger only so that trigger can refuse it loudly at bind time; lint 1d reports the shape itself).Suggested shape: have
resolveTriggerBindingtake itstriggerTypefromresolveFlowTriggerKind(flow)and keep only the binding construction per kind (the array-form diagnostic route can stay as an explicit pre-check before the resolver).getTriggerBindingAuditand the boot banner then name the same kinddefineStacknamed at authoring, by construction.Blocked-by: #14153 (the spec export lands there)
Found while implementing #14153 (the
defineStacktrigger-capability validator).Generated by Claude Code