fix(flows): stop condition nodes starving downstream =item + dry-run routing-divergence warning (B15)#4698
Conversation
…dry-run routing divergence (B15)
A downstream agent's input_context "=item" resolved NULL after a `condition` node → the
agent hallucinated generic content ("@JDoe"), and the dry-run reported 0 warnings. Root
cause was two coupled engine bugs (fixed in tinyflows #5, pin bumped here) + a dry-run
blind spot (fixed here):
- tinyflows A1: a `true`-only condition was lowered as an unconditional edge; collect_input
then dropped items on the emitted `false` port → successor ran with empty input. Now a
single non-`main` edge is a conditional edge with an END fallback → the condition is a
FILTER (items flow on true, run ends on false).
- tinyflows A2: the condition read `field` as a literal key, so `field:"=item.assignee"`
never matched → always false. Now it resolves `=`-expressions and checks the resolved
value's truthiness (literal fields unchanged).
- openhuman dry-run: DryRunWorkflowTool now emits a routing-divergence WARNING when an
agent/tool_call node never executed because a condition routed the mock data away — the
class the dry-run missed (mock item shape ≠ real). Pure warning, doesn't flip ok.
vendor/tinyflows pin -> 56d81df (tinyflows#5). tinyflows 304+12 tests pass; openhuman
cargo check clean; flows:: 392 pass (+ dry_run_warns_on_unexercised_agent_after_condition).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughDryRunWorkflowTool now detects "routing-divergence" cases where agent/tool_call nodes never execute because an upstream condition node takes a different branch under mock trigger input. A new ChangesRouting-Divergence Warning Feature
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fb47a46a22
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| @@ -1 +1 @@ | |||
| Subproject commit eb4865d1a16679b03fe79858c7b656adc34baa98 | |||
| Subproject commit 56d81dfdadc1454189322d744e050ae4f7e525f9 | |||
There was a problem hiding this comment.
Preserve fan-in barriers for single conditional edges
When this bumps TinyFlows to 56d81df, its new single non-main edge lowering routes directly with add_conditional_edges before the existing fan-in check. In a graph where a true-only condition feeds a merge/fan-in that also has another independent predecessor, the merge can now be scheduled as soon as the condition branch fires instead of waiting for the other predecessor, so it observes partial input and produces an incomplete merge. The default-main path in the same TinyFlows change still applies incoming_counts/add_waiting_edge, so the new conditional path needs equivalent fan-in handling for the taken branch.
Useful? React with 👍 / 👎.
Fixes B15 — a downstream node was silently starved after a
condition, so an agent'sinput_context:"=item"resolved null → it hallucinated generic content ("@JDoe", not the real data). Proven on the "funny reminders" flow: every run generated a fake reminder. Two coupled tinyflows engine bugs + a dry-run blind spot.Root cause (elegant — two bugs compound)
conditionwith only atrueedge was lowered as an unconditional edge → the successor always ran, butcollect_inputport-matchedtruevs the emittedfalseand dropped the items → the successor ran with empty input. (Every existing condition test wires both edges, so this single-edge path was never covered.)fieldwas read as a literal key, sofield:"=item.assignee"didget("=item.assignee")→ alwaysNone→ alwaysfalse. Combined with A1 → always-false + always-runs-empty →=itemnull.Fixes
56d81df): a single non-mainedge is now a conditional edge with anENDfallback → atrue-only condition acts as a filter (items flow on true, run ends on false); the condition node now resolves=-expressions infield.DryRunWorkflowToolnow emits a routing-divergence warning when anagent/tool_callnode never executed because a condition routed the mock data away — the exact class the dry-run missed (mock item shape ≠ real). Pure warning, doesn't flipok.Verification
tinyflows 304 lib + 12 doctests +
--all-featurese2e pass · openhumancargo checkclean ·flows::392 pass (+dry_run_warns_on_unexercised_agent_after_condition).Summary by CodeRabbit
Bug Fixes
Tests
Chores