Summary
A wait step's when guard is the last place in the DSL where a status NAME survives unresolved into generated code. StatusSymbolResolver rewrites a status symbol to its seed id for abortOn, process steps' setRelationField, postings, generates, resolves, reports, entity immutableWhen - and, as of #6906, a process trigger's when - but not a wait step's.
The result is a guard that can never hold:
processes:
- name: Dunning
trigger: { onTransition: Fine, when: "Status == IDENTIFIED" }
steps:
- { name: hold, kind: wait, args: { onTransition: Fine, when: "Status == NEW", next: remind } }
- { name: remind, kind: end }
renders (probed through GlueIntentGenerator.buildWaitsForTest on origin/master + #6906):
PROBE trigger=Dunning topic=-transitioned guard=[java.util.Objects.equals(entity.Status, 2)]
PROBE wait=Dunning topic=-transitioned guard=[java.util.Objects.equals(entity.Status, "NEW")]
The trigger's guard resolves; the wait's does not. Wait.java.template emits it verbatim, so the generated listener compares the integer status FK against the string "NEW" - always false, so Process.correlateMessageEvent is never reached and the parked instance sits there forever.
Why it hides
Every failure on this path is fail-soft by design ("no matching parked instance is a no-op, never an error"), so a guard that never holds is indistinguishable from an event that was not for this wait. Nothing is logged. The onTransition axis (#6810) makes this the normal thing to write - a wait that resumes when the record reaches a status - so the symbol form is exactly what an author reaches for.
The numeric form works today (when: "Status == 1"), which is presumably why it has not been noticed.
Fix candidate
StatusSymbolResolver.rewriteProcesses already walks each process's steps (for setRelationField + value). Resolve args.when for a kind: wait step in the same loop, against the status nomenclature of the step's OWN event entity (args.onCreate/onUpdate/onTransition), which is not necessarily the trigger entity - the via: case is exactly when they differ.
A test belongs next to the trigger one added in #6906 (GlueTransitionAxisTest), asserting the wait's guardExpression resolves to the seed id rather than the name.
Found while fixing #6862 (see #6906, which fixes the trigger half).
Summary
A
waitstep'swhenguard is the last place in the DSL where a status NAME survives unresolved into generated code.StatusSymbolResolverrewrites a status symbol to its seed id forabortOn, process steps'setRelationField, postings, generates, resolves, reports, entityimmutableWhen- and, as of #6906, a process trigger'swhen- but not awaitstep's.The result is a guard that can never hold:
renders (probed through
GlueIntentGenerator.buildWaitsForTestonorigin/master+ #6906):The trigger's guard resolves; the wait's does not.
Wait.java.templateemits it verbatim, so the generated listener compares the integer status FK against the string"NEW"- always false, soProcess.correlateMessageEventis never reached and the parked instance sits there forever.Why it hides
Every failure on this path is fail-soft by design ("no matching parked instance is a no-op, never an error"), so a guard that never holds is indistinguishable from an event that was not for this wait. Nothing is logged. The
onTransitionaxis (#6810) makes this the normal thing to write - a wait that resumes when the record reaches a status - so the symbol form is exactly what an author reaches for.The numeric form works today (
when: "Status == 1"), which is presumably why it has not been noticed.Fix candidate
StatusSymbolResolver.rewriteProcessesalready walks each process's steps (forsetRelationField+value). Resolveargs.whenfor akind: waitstep in the same loop, against the status nomenclature of the step's OWN event entity (args.onCreate/onUpdate/onTransition), which is not necessarily the trigger entity - thevia:case is exactly when they differ.A test belongs next to the trigger one added in #6906 (
GlueTransitionAxisTest), asserting the wait'sguardExpressionresolves to the seed id rather than the name.Found while fixing #6862 (see #6906, which fixes the trigger half).