Skip to content

A decision node's singular config.condition is never evaluated — 24 flows author inert metadata #650

Description

@os-zhuang

Found while implementing #643, deliberately left out of its scope.

What was measured

@objectstack/service-automation@17.0.0-rc.1 evaluates a flow condition in exactly three places:

  1. the start node's config.conditionAutomationEngine.execute;
  2. a decision node's config.conditions — the PLURAL, an array of { label, expression }, read by the decision node executor as const conditions = config?.conditions ?? [];
  3. every out-edge's conditionAutomationEngine.traverseNext.

Every decision node in this repo authors the singular config.condition instead:

{
  id: 'check_campaign_open', type: 'decision', label: 'Campaign Open?',
  config: { condition: P`vars.campaignRecord.status == "planning"` },
}

That key is never read. The executor finds conditions empty, returns branchLabel: 'default' immediately, and the branch is decided entirely by the edge copies of the same predicate.

Affected sites (grep type: 'decision' under src/flows/): campaign_enrollment (check_campaign_open, check_not_enrolled), lead_conversion (decision_account, decision_contact, decision_opportunity), quote_generation (check_stage), opportunity_approval + _on_create (check_high_value), contract_renewal (check_notice_window, check_not_reminded, check_auto_renewal, check_no_open_renewal), demo_bootstrap (has_user), opportunity_stagnation (check_not_nudged), forecast_snapshot (has_deals, check_missing), and the record-change decision nodes lead_assignment (check_hot) / case_escalation / etc.

Why it matters even though behaviour is correct today

Behaviour is correct only because every one of these predicates is duplicated onto the out-edges. Nothing enforces that duplication:

The has() guards added in #643 were applied to both copies for this reason, and test/flow-variable-conditions.test.ts sweeps node and edge conditions alike. But nothing yet asserts the two copies agree.

What to establish

  1. Is the singular config.condition on a decision node meant to be authorable at all? FlowNodeSchema.config is an open Record<string, unknown>, so it neither validates nor rejects the key — check whether Studio's designer form emits it, and whether the plural form is the only supported shape.
  2. If the singular form is legacy: either (a) convert every decision node to config.conditions: [{ label, expression }] so the node is the live site and the edges follow its labels, or (b) drop the node-level condition entirely and let the edges be the single source of truth. These are different architectures — (a) makes the node authoritative, (b) makes it edge-only — and the choice affects every flow in the repo.
  3. Whichever way it goes, add the assertion that is missing today: for a decision node, its condition and its out-edge conditions must not drift.

Do not assume the answer from the other three surfaces — that is the mistake #633 was opened to prevent.

Filed unassigned.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingmetadataDeclarative metadata — schema, security posture, UI surfacespm:queueReady for the PM dispatch loopprio:p1Next in line once P0s clear

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions