@@ -45,15 +45,15 @@ export const FLOW_TRIGGER_UNKNOWN_EVENT = 'flow-trigger-unknown-event';
4545type AnyRec = Record < string , unknown > ;
4646
4747/**
48- * Recognized record-change lifecycle ops. A start node's `triggerType` fires only
49- * when it matches `record-(before|after)-<op>` with `op` in this set — the exact
50- * grammar the record-change trigger's `triggerTypeToHookEvents` maps to ObjectQL
51- * hooks. `insert` is a synonym for `create`; `write` is the create-OR-update union
52- * (#3427). Kept in sync with that trigger (one small, stable contract).
48+ * The record-change trigger fires only for a `triggerType` matching this exact
49+ * grammar — the same set its `triggerTypeToHookEvents` maps to ObjectQL hooks.
50+ * `insert` is a synonym for `create`; `write` is the create-OR-update union
51+ * (#3427). Any OTHER `record-`-prefixed token — a typo (`record-after-updated`),
52+ * a phase-less bare noun (`record-change`), or a bad phase (`record-during-update`)
53+ * — binds to the trigger but maps to NO hook and never fires. Kept in sync with
54+ * that trigger (one small, stable contract).
5355 */
54- const RECORD_TRIGGER_OPS = new Set ( [ 'create' , 'insert' , 'update' , 'delete' , 'write' ] ) ;
55- /** A record-lifecycle-SHAPED token: `record-before-…` / `record-after-…`. */
56- const RECORD_EVENT_SHAPE = / ^ r e c o r d - (?: b e f o r e | a f t e r ) - ( .+ ) $ / ;
56+ const VALID_RECORD_TRIGGER = / ^ r e c o r d - (?: b e f o r e | a f t e r ) - (?: c r e a t e | i n s e r t | u p d a t e | d e l e t e | w r i t e ) $ / ;
5757
5858/** Coerce an array-or-name-keyed-map collection to an array (name injected). */
5959function asArray ( v : unknown ) : AnyRec [ ] {
@@ -142,29 +142,25 @@ export function validateFlowTriggerReadiness(stack: AnyRec): FlowTriggerReadines
142142 }
143143 }
144144
145- // 1c. Record-lifecycle-SHAPED triggerType (`record-before|after-…`) whose op
146- // is not one the trigger can map — a typo like `record-after-updated`. The
147- // engine still routes any `record-` token to the record-change trigger,
148- // which then maps it to NO hook and never fires (only a runtime warn). This
149- // is a definite never-fire defect, so surface it at authoring time. (Bare
150- // `record-<noun>` shapes without a before/after phase — e.g. `record-change`
151- // — are a separate concern and not flagged here.)
152- if ( start && triggerType ) {
153- const shape = RECORD_EVENT_SHAPE . exec ( triggerType ) ;
154- if ( shape && ! RECORD_TRIGGER_OPS . has ( shape [ 1 ] ) ) {
155- findings . push ( {
156- severity : 'warning' ,
157- rule : FLOW_TRIGGER_UNKNOWN_EVENT ,
158- where : `flow "${ flowName } " › start node` ,
159- path : `flows[${ flowIndex } ].nodes[${ start . index } ].config.triggerType` ,
160- message :
161- `triggerType '${ triggerType } ' names an unrecognized lifecycle event '${ shape [ 1 ] } ' — the flow binds to ` +
162- `the record-change trigger but never fires (the runtime stays silent about it).` ,
163- hint :
164- `Use record-{before,after}-{create,update,delete,write}. 'write' fires on create OR update in one ` +
165- `flow (#3427); create/insert are synonyms.` ,
166- } ) ;
167- }
145+ // 1c. A `record-`-prefixed triggerType the trigger cannot map to any hook —
146+ // a typo (`record-after-updated`), a phase-less bare noun (`record-change`,
147+ // which the Studio picker once offered as "Record changed (any)"), or a bad
148+ // phase (`record-during-update`). The engine routes any `record-` token to
149+ // the record-change trigger, which then binds to NO hook and never fires
150+ // (only a runtime warn). Surface the never-fire defect at authoring time.
151+ if ( start && isRecordTriggered && ! VALID_RECORD_TRIGGER . test ( ( triggerType ?? '' ) . trim ( ) ) ) {
152+ findings . push ( {
153+ severity : 'warning' ,
154+ rule : FLOW_TRIGGER_UNKNOWN_EVENT ,
155+ where : `flow "${ flowName } " › start node` ,
156+ path : `flows[${ flowIndex } ].nodes[${ start . index } ].config.triggerType` ,
157+ message :
158+ `triggerType '${ triggerType } ' is not a recognized record trigger — the flow binds to the ` +
159+ `record-change trigger but never fires (the runtime stays silent about it).` ,
160+ hint :
161+ `Use record-{before,after}-{create,update,delete,write}. 'write' fires on create OR update in one ` +
162+ `flow (#3427); create/insert are synonyms. There is no "any change" token — pick the specific event(s).` ,
163+ } ) ;
168164 }
169165
170166 // 2. Auto-triggered flow whose status is 'draft' — authored or defaulted
0 commit comments