|
| 1 | +--- |
| 2 | +"@objectstack/service-automation": patch |
| 3 | +--- |
| 4 | + |
| 5 | +refactor(service-automation): take the trigger KIND from spec's `resolveFlowTriggerKind` instead of a second private copy of the chain (#14328) |
| 6 | + |
| 7 | +No behaviour change and no API change — `patch` because nothing observable moves. |
| 8 | +`resolveTriggerBinding` is `private`, no export is added or removed, no payload |
| 9 | +key changes, and the kind reported for every flow is the kind reported before |
| 10 | +(1,223 `service-automation` cases and 81 `trigger-record-change` cases green |
| 11 | +unchanged, plus new pins across the whole precedence chain). What changes is that |
| 12 | +one rule now has one home. |
| 13 | + |
| 14 | +**The defect.** `AutomationEngine.resolveTriggerBinding` hand-kept the chain that |
| 15 | +decides which trigger a flow asks for — string `record-*` token, array form, |
| 16 | +`timeRelative` descriptor, `schedule` cadence or `type: 'schedule'`, `type: 'api'` |
| 17 | +or `triggerType: 'api'` — in parallel with `@objectstack/spec`'s |
| 18 | +`resolveFlowTriggerKind`, the authoring-time mirror of that same rule. Both |
| 19 | +authoring surfaces already read the spec one: `defineStack`'s trigger-capability |
| 20 | +refusal and `@objectstack/lint`'s `validate-flow-trigger-readiness`. The engine |
| 21 | +did not, and nothing pinned the two together. A branch added to one side leaves |
| 22 | +`defineStack` accepting a stack the runtime leaves inert, or refusing one it would |
| 23 | +arm — the drift the shared resolver was hoisted to prevent, reopened one layer |
| 24 | +down. The two agreed on every string-form flow, so this was an observation rather |
| 25 | +than a live defect; the harm was future drift. |
| 26 | + |
| 27 | +**The shape.** `resolveTriggerBinding` now takes its kind from |
| 28 | +`resolveFlowTriggerKind(flow)` and keeps only the per-kind BINDING construction — |
| 29 | +which start-node fields each trigger needs. `getTriggerBindingAudit` and the boot |
| 30 | +banner therefore name the kind authoring named, by construction. |
| 31 | + |
| 32 | +**The one deliberate divergence is preserved, not unified.** The ARRAY form of |
| 33 | +`triggerType` (`['record-after-create', 'record-after-delete']`) resolves to *no* |
| 34 | +kind in spec — multi-event unions are unsupported (#3457), and reading the shape |
| 35 | +as "asks for a record-change trigger" would have `defineStack` demand a capability |
| 36 | +the flow can never use and would widen the lint rule's auto-triggered set. The |
| 37 | +engine routes it to the record-change trigger anyway, from an explicit pre-check |
| 38 | +that runs BEFORE the resolver, for one reason: so that trigger refuses it LOUDLY |
| 39 | +at bind time (#3481) instead of the flow folding into "manual" and vanishing from |
| 40 | +every surface. Pre-check *ordering* is load-bearing too — array form outranks |
| 41 | +`timeRelative`, which the resolver, blind to the array, would otherwise answer for |
| 42 | +a start node carrying both. |
| 43 | + |
| 44 | +**What now catches the drift.** Two guards, one static and one runtime. The |
| 45 | +per-kind `switch` is exhaustive over `FlowTriggerKind` with a `never` default, so a |
| 46 | +kind added to spec fails this package's type-check until its binding shape is |
| 47 | +written; and a new case asserts every kind in `FLOW_TRIGGER_KINDS` is reachable |
| 48 | +through the real engine. The preserved divergence is pinned on both sides: engine |
| 49 | +routing and pre-check precedence in `service-automation`, and the refusal itself — |
| 50 | +asserted as a refusal, on a binding the real engine produced — end-to-end against |
| 51 | +the real trigger in `@objectstack/trigger-record-change`. |
0 commit comments