Skip to content

A decision node with no declared config.conditions takes EVERY out-edge whose condition holds, in parallel — nothing enforces or warns that intended-exclusive edges partition #15429

Description

@os-zhuang

Split out of #14945 by the triage seat (R+145). The filing repo:hotcrm seat named it there and asked triage to judge whether the two share a cause. They do not: #14945 is an expressiveness gap (a flow cannot author a per-record refusal), this is a silent behavioural hazard in decision-node evaluation. Different question, different fix, different lane risk.

What was measured

A decision node with no declared config.conditions evaluates every out-edge and takes all of those whose condition holds — in parallel, not first-match.

Found the hard way in objectstack-ai/hotcrm#1555: a Clean edge spelled != "suspected" and a newly added == "confirmed" edge were both live for a confirmed record, so a refusal screen would have rendered and the conversion would have run in the same execution.

It was found by an ablation, not by review. Two edges out of one decision node, each individually sensible, whose conditions silently overlap — and nothing in the platform reports it.

Why it is worth a card

An author writing a decision node almost always intends a partition: exactly one branch is taken. Nothing enforces that, nothing warns about it, and the failure is silent and behavioural — both branches simply run.

⇒ This is the shape the platform's own error-proofing doctrine ranks worst: an author declares something that reads as exclusive, the runtime does something else, and there is no signal at author time, build time or run time. The repair in hotcrm was to narrow the Clean condition into a true partition by hand, which is exactly the knowledge that does not survive into the next flow anyone writes.

What a fix would decide, ⛔ not decided here

  • Author-time / build-time: a lint or os build diagnostic when a decision node's out-edge conditions are not provably disjoint. ⚠️ Provable disjointness over CEL is not free — establish what is decidable before promising it, and prefer a narrow, honest check over a clever one.
  • Runtime: report when more than one out-edge fires on a node that declared no conditions. Cheap and certain, but it fires after the fact.
  • Contract: make the exclusive case the declarable one, so an author can say "these partition" and be held to it.

⛔ Do not assume the answer is "first match wins" — changing evaluation semantics on a shipped node type is a behaviour change with its own ruling, ⛔ not a bug fix.

First step

Establish the current semantics from source rather than from this card: whether "no config.conditions" is a distinct mode from "declared conditions", and whether the parallel take is intended or incidental. The hotcrm evidence is one reproduction on one deployment, ⛔ not a reading of the engine.

priority:p2: no known data loss, but a silent multi-branch execution in a flow that writes records is a defect class that gets worse with every flow authored. ⇒ domain:servicesservice-automation owns flow execution.

Refs: #14945 (where it was reported) · objectstack-ai/hotcrm#1555 (the reproduction and the hand repair) · objectstack-ai/hotcrm#1288 (the ruling that surfaced it).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions