You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
FlowVariableSchema has no defaultValue, so a flow variable cannot be declared bound — the obvious remedy for an unbound-variable condition does not exist #651
Found while implementing #643, which prescribed a remedy that turned out not to be expressible.
The gap
#643 asked that an unbound flow variable be fixed by "declaring the variable with a default so it is always bound", rather than by a has() guard. Measured against @objectstack/spec@17.0.0-rc.1, neither half of that is available:
1. The schema has no such key.FlowVariableSchema is z.core.$strict over exactly four fields:
So declaring a variable in flow.variables binds nothing at runtime. A declared input is unbound on every run whose caller did not pass it, and a strict-CEL condition reading it then aborts with No such key: <name> (Unknown variable: <name> for the bare spelling), the run is recorded failed, and the automation does not happen. That is the exact live defect #643 fixed in lead_conversion.
That achieves the right semantics — bound on every path, guaranteed by the graph rather than by the client — using the mechanism the platform actually has. But it is a workaround in three ways:
The default now lives twice: once on the screen field's defaultValue, once on the assignment node. Nothing keeps them in step.
It costs a node and an edge per defaulted variable, in a graph where nodes are the unit of review.
It would clobber a genuinely-supplied context.params value, because an assignment node is unconditional. Harmless here — the console's flow-action trigger sends only { recordId, objectName }, so createOpportunity cannot arrive as a param — but it is not the general shape.
test/flow-variable-conditions.test.ts pins the structural property (every variable a condition reads is bound on every path to it) so the gap cannot silently reappear, and its house-rule block records "declaring ≠ binding" as the trap it is.
What to establish
Is defaultValue on a flow variable an intended-but-missing spec feature, or is the graph-binds-it model deliberate? If deliberate, the authoring story needs to say so — right now flow.variables reads exactly like a declaration that binds, which is what made this a defect rather than a known limitation.
If it should exist: it belongs upstream in packages/spec (FlowVariableSchema) and in service-automation's execute/executeWithoutRetry seeding, applied before the start condition is evaluated so a start condition can read a defaulted variable too. Both halves are needed — a schema key with no engine support would be the inert-metadata failure of A decision node's singular config.condition is never evaluated — 24 flows author inert metadata #650.
Decide whether a screen field's defaultValue should seed the flow variable of the same name at suspend time, which would remove the duplication above without any new spec key.
This is a platform-side gap; HotCRM can only work around it. Filing here so the workaround in lead_conversion has something to point at and can be removed when the platform closes it.
Found while implementing #643, which prescribed a remedy that turned out not to be expressible.
The gap
#643 asked that an unbound flow variable be fixed by "declaring the variable with a default so it is always bound", rather than by a
has()guard. Measured against@objectstack/spec@17.0.0-rc.1, neither half of that is available:1. The schema has no such key.
FlowVariableSchemaisz.core.$strictover exactly four fields:Being strict, adding
defaultValueto a flow variable is a parse error, not an ignored key.2. The engine has no default-application step.
AutomationEngine.executebinds a declared variable only when the caller supplied it:So declaring a variable in
flow.variablesbinds nothing at runtime. A declared input is unbound on every run whose caller did not pass it, and a strict-CEL condition reading it then aborts withNo such key: <name>(Unknown variable: <name>for the bare spelling), the run is recordedfailed, and the automation does not happen. That is the exact live defect #643 fixed inlead_conversion.What #643 did instead, and why it is a workaround
lead_conversionnow seeds the variable with anassignmentnode placed ahead of the screen that collects it:That achieves the right semantics — bound on every path, guaranteed by the graph rather than by the client — using the mechanism the platform actually has. But it is a workaround in three ways:
defaultValue, once on the assignment node. Nothing keeps them in step.context.paramsvalue, because anassignmentnode is unconditional. Harmless here — the console's flow-action trigger sends only{ recordId, objectName }, socreateOpportunitycannot arrive as a param — but it is not the general shape.test/flow-variable-conditions.test.tspins the structural property (every variable a condition reads is bound on every path to it) so the gap cannot silently reappear, and its house-rule block records "declaring ≠ binding" as the trap it is.What to establish
defaultValueon a flow variable an intended-but-missing spec feature, or is the graph-binds-it model deliberate? If deliberate, the authoring story needs to say so — right nowflow.variablesreads exactly like a declaration that binds, which is what made this a defect rather than a known limitation.packages/spec(FlowVariableSchema) and inservice-automation'sexecute/executeWithoutRetryseeding, applied before the start condition is evaluated so a start condition can read a defaulted variable too. Both halves are needed — a schema key with no engine support would be the inert-metadata failure of Adecisionnode's singularconfig.conditionis never evaluated — 24 flows author inert metadata #650.defaultValueshould seed the flow variable of the same name at suspend time, which would remove the duplication above without any new spec key.This is a platform-side gap; HotCRM can only work around it. Filing here so the workaround in
lead_conversionhas something to point at and can be removed when the platform closes it.Filed unassigned.