fix(workflows): namespace nested descendant step ids in loops/fan-out - #4338
Open
Noor-ul-ain001 wants to merge 1 commit into
Open
fix(workflows): namespace nested descendant step ids in loops/fan-out#4338Noor-ul-ain001 wants to merge 1 commit into
Noor-ul-ain001 wants to merge 1 commit into
Conversation
`while`/`do-while` loop bodies and `fan-out` templates namespace nested
step ids per iteration/item so logs and `state.step_results` entries stay
unique — but the namespacing only rewrote the id of the *immediate* child
step, not any descendant nested deeper (e.g. a `shell` step inside an `if`
inside a `while` body, or inside a `fan-out` template's `if`/`switch`
branch). That grandchild kept its bare, unnamespaced id across every
iteration/item, so each iteration/item silently overwrote the previous
one's entry in `state.step_results` under that same key — only the last
iteration's or item's result for that nested step ever survived, and no
per-iteration/per-item record of it ever existed.
This is also a correctness gap beyond bookkeeping: nested/template step ids
are deliberately exempted from the workflow's global id-uniqueness
validation, on the assumption that runtime namespacing makes any collision
safe. Since only the top-level child was actually namespaced, a step
nested one level deeper could collide with an unrelated step of the same
id elsewhere in the workflow and silently overwrite its result.
Fix: add `_rename_step_tree_ids`, which recursively rewrites every id in a
step's subtree (walking `then`/`else`/`steps`/`default`/`cases.*` — the
same nesting keys `overlays/merge.py` walks for step-tree attribution) and
returns a `{new_id: original_id}` map. Both the while/do-while loop body
and fan-out's `run_item` now use this helper instead of renaming only the
top-level id, and alias every renamed descendant's result back to its
original id (mirroring the existing single-level aliasing) so sibling
steps within the same iteration/item and code reading `steps.<id>.output`
after the loop/fan-out still see that iteration's/item's value.
## Test plan
- Added `test_while_loop_namespaces_nested_descendant_steps` and
`test_fan_out_namespaces_nested_descendant_steps` to
`tests/test_workflows.py::TestWorkflowEngine`: a `shell` step nested
inside an `if` inside a `while` body (and inside a `fan-out` template)
gets a distinct namespaced `state.step_results` entry per
iteration/item, while the unprefixed key still holds the latest value.
- Verified both fail without the fix (test-the-test): the namespaced keys
(`retry-loop:leaf:1`, `fan:leaf:0`, etc.) were simply absent, and
`step_results` only ever held the last iteration's/item's bare-keyed
entry — reproducing the exact bug.
- Ran the full `tests/test_workflows.py` suite: 926 passed, 20 pre-existing
Windows symlink-elevation failures (need admin rights, unrelated to this
change), 7 skipped. All `While`/`DoWhile`/`FanOut`/`FanOutConcurrency`
tests pass, including the concurrent-execution and per-thread context
isolation tests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJHJ2dHP2RVCNncHqN8Qm9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
while/do-whileloop bodies andfan-outtemplates namespace nested step ids per iteration/item so logs andstate.step_resultsentries stay unique — but the namespacing only rewrote the id of the immediate child step, not any descendant nested deeper (e.g. ashellstep inside anifinside awhilebody, or inside afan-outtemplate'sif/switchbranch). That grandchild kept its bare, unnamespaced id across every iteration/item, so each iteration/item silently overwrote the previous one's entry instate.step_resultsunder that same key — only the last iteration's or item's result for that nested step ever survived, and no per-iteration/per-item record of it ever existed._rename_step_tree_ids, which recursively rewrites every id in a step's subtree (walkingthen/else/steps/default/cases.*— the same nesting keysoverlays/merge.pywalks for step-tree attribution) and returns a{new_id: original_id}map. Both the while/do-while loop body and fan-out'srun_itemnow use this helper instead of renaming only the top-level id, and alias every renamed descendant's result back to its original id (mirroring the existing single-level aliasing) so sibling steps within the same iteration/item and code readingsteps.<id>.outputafter the loop/fan-out still see that iteration's/item's value.Test plan
test_while_loop_namespaces_nested_descendant_stepsandtest_fan_out_namespaces_nested_descendant_stepstotests/test_workflows.py::TestWorkflowEngine: ashellstep nested inside anifinside awhilebody (and inside afan-outtemplate) gets a distinct namespacedstate.step_resultsentry per iteration/item, while the unprefixed key still holds the latest value.retry-loop:leaf:1,fan:leaf:0, etc.) were simply absent, andstep_resultsonly ever held the last iteration's/item's bare-keyed entry — reproducing the exact bug.tests/test_workflows.pysuite: 926 passed, 20 pre-existing Windows symlink-elevation failures (need admin rights, unrelated to this change), 7 skipped. AllWhile/DoWhile/FanOut/FanOutConcurrencytests pass, including the concurrent-execution and per-thread context isolation tests.Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
https://claude.ai/code/session_01PJHJ2dHP2RVCNncHqN8Qm9