Node debugger cannot resolve upstream outputs in manual evaluations #28

Closed
opened 2026-09-17 10:17:08 +02:00 by morten-olsen · 0 comments
Owner

Context

The node debugger's Test tab could not evaluate any node whose configuration reads an earlier step's output. Pressing Evaluate node failed before running:

invalid_switch_condition: Switch rule 'Charged and powered' failed:
Output battery/main is not available.

NodeDebugCodec.manualPorts built its NodeDebugInput with upstreamOutputs = emptyMap(), and NodeDebugger passed that straight into NodeExecutionContext.outputs, so every OutputValue(nodeId, portId, path) reference was unresolvable. Retained run inputs worked, because decodeInput restores the captured upstream map; manual inputs never did.

This affected any node with an output binding, not only Switch. #12 made output-based comparisons the norm for switch branches, which is what surfaced it.

Scope

  • Derive the upstream ports a node reads, from configuration values and from switch conditions, which live inside the opaque rules literal that no generic template walk can see into.
  • Let a manual evaluation supply those values, seeded from each referenced port's declared schema so the author edits a filled record rather than writing one blind.
  • Name each referenced port by its producing step, the way the route names it.

Non-goals

  • Do not run ancestor nodes to satisfy a reference. Executing them the way FixtureWorkflowRunner does would fire real Read effects on pressing Evaluate node, against the panel's own promise that only this step runs.
  • Do not change the retained-run path, which already carries its own upstream values.

Resolution

Fixed on main in abcf262.

  • ValueTemplateEvaluator.outputReferences answers which upstream port a value comes from, beside the existing dependencies, which answers only which nodes must run first. It lives there because the evaluator already owns the reference syntax.
  • WorkflowNode.upstreamReferences is the single reader of what a node reads upstream, folding in switch conditions explicitly. The compiler, authoring validation and the debugger now cannot disagree about which references exist.
  • The panel renders an Upstream values block per referenced port, falling back to the producing action's output schema because node-shape snapshots only carry schemas for configuration-derived ports.

Seeding logic sits in its own file rather than the composable so it can be tested — this module has no Compose test harness. Covered by NodeDebuggerUpstreamTest, UpstreamReferencesTest and UpstreamSkeletonTest, including the before/after pair pinning the original failure. Verified on a Pixel 9 emulator: the same switch node that failed now evaluates to case:healthy · 0 records, case:critical · 0 records, fallback · 1 record.

## Context The node debugger's **Test** tab could not evaluate any node whose configuration reads an earlier step's output. Pressing **Evaluate node** failed before running: ``` invalid_switch_condition: Switch rule 'Charged and powered' failed: Output battery/main is not available. ``` `NodeDebugCodec.manualPorts` built its `NodeDebugInput` with `upstreamOutputs = emptyMap()`, and `NodeDebugger` passed that straight into `NodeExecutionContext.outputs`, so every `OutputValue(nodeId, portId, path)` reference was unresolvable. Retained run inputs worked, because `decodeInput` restores the captured `upstream` map; manual inputs never did. This affected **any** node with an output binding, not only Switch. #12 made output-based comparisons the norm for switch branches, which is what surfaced it. ## Scope - Derive the upstream ports a node reads, from configuration values and from switch conditions, which live inside the opaque `rules` literal that no generic template walk can see into. - Let a manual evaluation supply those values, seeded from each referenced port's declared schema so the author edits a filled record rather than writing one blind. - Name each referenced port by its producing step, the way the route names it. ## Non-goals - Do not run ancestor nodes to satisfy a reference. Executing them the way `FixtureWorkflowRunner` does would fire real Read effects on pressing **Evaluate node**, against the panel's own promise that only this step runs. - Do not change the retained-run path, which already carries its own upstream values. ## Resolution Fixed on `main` in abcf262. - `ValueTemplateEvaluator.outputReferences` answers which upstream *port* a value comes from, beside the existing `dependencies`, which answers only which nodes must run first. It lives there because the evaluator already owns the reference syntax. - `WorkflowNode.upstreamReferences` is the single reader of what a node reads upstream, folding in switch conditions explicitly. The compiler, authoring validation and the debugger now cannot disagree about which references exist. - The panel renders an **Upstream values** block per referenced port, falling back to the producing action's output schema because node-shape snapshots only carry schemas for configuration-derived ports. Seeding logic sits in its own file rather than the composable so it can be tested — this module has no Compose test harness. Covered by `NodeDebuggerUpstreamTest`, `UpstreamReferencesTest` and `UpstreamSkeletonTest`, including the before/after pair pinning the original failure. Verified on a Pixel 9 emulator: the same switch node that failed now evaluates to `case:healthy · 0 records`, `case:critical · 0 records`, `fallback · 1 record`.
morten-olsen 2026-09-17 10:17:08 +02:00
  • closed this issue
  • added the
    bug
    label
Sign in to join this conversation.
No labels
bug
enhancement
feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
incubator/macros#28
No description provided.