Skip to content

Merge Flow node may feed downstream nodes stale/empty input during Play All (disablePlay short-circuit) #151

Description

@GMMULLER

Summary

A Merge Flow node may signal "done" during Play All before it has propagated its merged output to downstream nodes, so a downstream node can execute with stale/empty input (arg = None).

On the datacatalog branch the merge behavior returns disablePlay: true. In UniversalNode, the Play-All triggerExec handler short-circuits on disablePlay — it calls signalNodeExecDone(...) and returns before invoking the merge's sendCodeOverride (the synchronous emitter that pushes the merged output downstream). signalNodeExecDone then synchronously advances the run to the next level (triggerLevel), bumping the downstream node's triggerExec. The merge's output now only reaches downstream data.input via its reactive useEffectoutputCallbackpropagateDownstreamInputs, which commits on a later React render — potentially after the downstream node has already executed.

In main, the merge behavior exposed sendCodeOverride without disablePlay, so the merge emitted synchronously on the Play-All path and signalNodeExecDone fired only after propagation (the same pattern Data Pool still uses).

Note: confirmed as a real mechanism by code reading, but the exact React render/commit/effect interleaving has not been reproduced at runtime — marked Discussion required so it can be verified before/while fixing.

Steps to Reproduce

  1. Wire two source nodes into a Merge Flow node.
  2. Wire a downstream node (e.g. a Python/JS transform) to the merge's output.
  3. Click Play All (rather than running nodes individually).
  4. Inspect the input the downstream node receives on that run.

Expected Result

The downstream node receives the merge's merged output as its input on the Play-All run — the merge propagates its output before the run advances to the downstream level.

Actual Result

The merge short-circuits (disablePlaysignalNodeExecDone + early return), the run advances to the downstream node before the merge's reactive propagation commits, and the downstream node can execute with stale/empty input (arg = None). Running the downstream node manually afterwards works, because by then propagation has committed.

Environment

  • Branch: datacatalog (b6979cb)
  • Frontend: urban-workflows (local dev), Chromium-based browser
  • Identified via static code review; runtime reproduction pending.

Additional Information

  • Code references:
    • utk_curio/frontend/urban-workflows/src/adapters/node/mergeFlowBehavior.ts — return block adds disablePlay: true (not present in main); sendCodeOverride / reactive emit effect.
    • utk_curio/frontend/urban-workflows/src/components/UniversalNode.tsxif (disablePlay || !sendCode) { signalNodeExecDone(...); return; } runs before sendCode on the triggerExec path.
    • utk_curio/frontend/urban-workflows/src/providers/FlowProvider.tsxsignalNodeExecDonetriggerLevel advances synchronously; applyNewOutputpropagateDownstreamInputs is the propagation path.
    • Reference pattern: utk_curio/frontend/urban-workflows/src/adapters/node/dataPoolBehavior.tsx exposes sendCodeOverride and does not set disablePlay.
  • Suggested direction: drop disablePlay: true from the merge behavior and rely on sendCodeOverride to emit synchronously during Play All (so signalNodeExecDone fires after propagation), matching Data Pool.
  • Likely related to (but distinct from) Clicking Play on a node during Play All freezes the workflow #127 "Clicking Play on a node during Play All freezes the workflow". Not one of issues Catalog page Dataset-details sidebar: blank preview and non-functional Sample-data / Close buttons #137Tech-debt: efficiency & de-duplication #147.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status
Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions