Skip to content

fix(argo): order conditional input fallback by DAG#3233

Merged
talsperre merged 1 commit into
masterfrom
fix-argo-conditional-input-paths
Jun 11, 2026
Merged

fix(argo): order conditional input fallback by DAG#3233
talsperre merged 1 commit into
masterfrom
fix-argo-conditional-input-paths

Conversation

@talsperre

Copy link
Copy Markdown
Collaborator

Summary

  • order Argo split-switch input candidates by DAG position before passing them to conditional_input_paths
  • make conditional_input_paths choose the latest executed skippable predecessor when all executed inputs are skippable
  • compare exact pathspec step names instead of substring matching step names in paths

Fixes #3230

Tests

  • uv run --with '.[dev]' python -m pytest test/unit/test_argo_conditional_input_paths.py test/unit/test_argo_workflows_cli.py -q
  • uv run --with pre-commit pre-commit run --files metaflow/plugins/argo/conditional_input_paths.py metaflow/plugins/argo/argo_workflows.py test/unit/test_argo_conditional_input_paths.py test/unit/test_argo_workflows_cli.py

@talsperre
talsperre force-pushed the fix-argo-conditional-input-paths branch 2 times, most recently from 8f040c7 to b411cf4 Compare June 3, 2026 01:27
@talsperre
talsperre force-pushed the fix-argo-conditional-input-paths branch from b411cf4 to 3d6c4eb Compare June 3, 2026 02:17
]
return len(cond_in_funcs) > 1 and len(cond_in_funcs) == len(node.in_funcs)

def _skippable_input_steps_in_dag_order(self, node):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes pass my existing test cases for conditionals so should be fine from that perspective.

@talsperre
talsperre marked this pull request as ready for review June 10, 2026 23:01
@greptile-apps

greptile-apps Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a bug where chained split-switch steps in an Argo workflow could cause the wrong predecessor path to be selected at a conditional join. Three targeted changes address the root cause.

  • DAG-ordered skippable steps: A new _skippable_input_steps_in_dag_order helper sorts split-switch in-funcs by their topological index (reversed), replacing the implicit iteration order that was previously relied upon.
  • Exact step-name matching: The old step in path substring check is replaced by _step_name_from_path extraction that compares step-name segments exactly, fixing false positives when one step name is a prefix of another.
  • Test coverage: A new unit test file reproduces the chained-skip regression with a real FlowSpec fixture and covers both the DAG-ordering assertion and the exact-name filtering parametrically.

Confidence Score: 5/5

All three changes are narrowly scoped to the conditional-input-path selection logic, and the new test directly reproduces the reported regression.

The logic is correct across all traced scenarios: chained skips, partial execution, and the step-name prefix collision that triggered the original bug. The two observations are cosmetic and do not affect runtime behaviour.

No files require special attention; conditional_input_paths.py carries the core logic change and is well-covered by the new tests.

Important Files Changed

Filename Overview
metaflow/plugins/argo/argo_workflows.py Extracts skippable in-funcs filtering into _skippable_input_steps_in_dag_order, which now sorts by topological position (reversed) so the earliest-in-argument-list entry is always the latest DAG predecessor.
metaflow/plugins/argo/conditional_input_paths.py Fixes substring-matching bug with exact step-name extraction via _step_name_from_path; adds ordered lookup in paths_by_step to pick the latest executed skippable predecessor; filters empty strings from skippable_steps.
test/unit/test_argo_conditional_input_paths.py New test file covering the chained-skip regression and the exact-name matching fix; uses a real FlowSpec fixture with mocked compilation.

Reviews (1): Last reviewed commit: "fix: order Argo conditional input fallba..." | Re-trigger Greptile

Comment on lines 36 to +38
# If the input-path is from a conditional, we want to pick the one that is last-in-line in the DAG.
# The order of graph parsing ensures that the steps are in reverse order of occurrence, so the first one is the latest.
latest_conditional_in_graph = trimmed[:1]
# Argo passes skippable_steps in reverse DAG order, so the first matching
# executed skippable step is the latest conditional predecessor.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The comment attributes the reverse-DAG ordering to "Argo", but it's actually _skippable_input_steps_in_dag_order in the compilation code that guarantees this order. A reader of this standalone script has no way to verify that contract from the comment alone. Tightening the wording makes the invariant self-documenting.

Suggested change
# If the input-path is from a conditional, we want to pick the one that is last-in-line in the DAG.
# The order of graph parsing ensures that the steps are in reverse order of occurrence, so the first one is the latest.
latest_conditional_in_graph = trimmed[:1]
# Argo passes skippable_steps in reverse DAG order, so the first matching
# executed skippable step is the latest conditional predecessor.
# If the input-path is from a conditional, we want to pick the one that is last-in-line in the DAG.
# skippable_steps is passed in reverse DAG order (ensured by _skippable_input_steps_in_dag_order),
# so the first matching executed step is the latest conditional predecessor.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +32 to +34
paths_by_step = {}
for path in trimmed:
paths_by_step.setdefault(_step_name_from_path(path), path)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 When path contains no / separator, _step_name_from_path returns None, which is silently inserted as a key in paths_by_step. This is harmless today (the None key is never looked up via skippable_steps), but it is an unexpected state. A guard keeps the dict's invariant explicit and prevents surprises if the code evolves.

Suggested change
paths_by_step = {}
for path in trimmed:
paths_by_step.setdefault(_step_name_from_path(path), path)
paths_by_step = {}
for path in trimmed:
step_name = _step_name_from_path(path)
if step_name is not None:
paths_by_step.setdefault(step_name, path)

@talsperre
talsperre merged commit dddc0d5 into master Jun 11, 2026
47 checks passed
@talsperre
talsperre deleted the fix-argo-conditional-input-paths branch June 11, 2026 06:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Argo: artifacts silently lost when a step has 2+ split-switch in_funcs that both ran

2 participants