Workflow editor: connection model, loop support, model escalation, and dashboard cleanup #4
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Several design issues surfaced while reviewing the service_agent workflow in the editor.
1. Remove Examples section from dashboard
The dashboard (
index.html) shows a full Examples section (table with Name/Workflow/Description/Updated columns) that takes significant vertical space. This was never requested and clutters the main view. There's also a separate/examplespage.Action: Remove the Examples section from the dashboard index page. Keep the
/examplesroute and the per-workflow example sidebar — those are useful in context.2. Connection visual style is misleading
The editor currently shows conditional edges as red dashed lines (
on_failure→ red,conditional→ amber dashed) vs solid grey foron_success. This creates a false impression that connections are fundamentally different types.In reality, every connection is the same type — it's just a pipe with a condition expression that gates whether data flows through. The visual distinction should communicate what condition is set, not imply different connection kinds.
Action: Unify the visual style. All connections are solid lines of the same base style. The condition label (shown on hover or always) communicates the gate. A small icon or badge on the edge can indicate it has a condition, but the line itself shouldn't change shape/color in a way that suggests different pipe types. Reserve dashed style only for "being drawn" (drag preview).
3. Script execution needs structured output with exit code
Currently, when hero_proc runs a script:
Success, stdout captured asoutput_dataFailed, stderr/stdout goes toerror_message,output_datais emptyThis means downstream conditional edges can only branch on
Success/Failed(viaon_success/on_failure), not on the actual exit code or stderr content. The error output isn't available as structured data for downstream nodes.Action: For script-type actions, produce structured output that includes
exit_code,stdout, andstderras separate fields. Whenoutput_formatistext(current default for scripts), wrap the result:This lets downstream edges use condition expressions like
outputs.exit_code == 0and data mappings can pullstdoutorstderrindependently.For the service_agent template specifically:
script_execution → result_summary: conditionoutputs.exit_code == 0, mapstdouttoexecution_outputscript_execution → error_debug: conditionoutputs.exit_code != 0, mapstderrtoerror_output, also carrystdoutfor context4. Error debug should loop back to code_generation, not a separate retry node
The v3 template has a separate
retry_executionnode that's essentially a duplicate ofscript_execution. This is wrong. The error_debug node should produce feedback and feed it back tocode_generationas additional context, which regenerates the script, which goes back toscript_execution. This creates a natural loop:The
error_debugnode's output becomes afeedbackinput oncode_generation's next iteration — containing the error, the failed script, and the AI's analysis of what went wrong.Problem: The current executor explicitly rejects cycles:
And
topological_sort()also fails on cycles. Supporting loops requires executor changes — see next section.5. Loop support in the executor
Loops are a natural pattern (retry with feedback, iterative refinement, polling). The question is how to support them without adding special-case complexity.
Proposed approach: keep it uniform
Rather than introducing a special "loop box" construct, treat loops as regular graph cycles with a loop counter tracked per-node:
iterationcounter (starts at 0, incremented each time the node runs again)iteration— e.g.,iteration < 3on the back-edge from error_debug to code_generationiteration >= 3→ skip to result_summary with the errorThis keeps the model uniform: no new node types, no special constructs. A loop is just a cycle in the graph where at least one edge has an iteration-based condition.
Executor changes needed:
validate_workflow()should allow cycles, but verify that every cycle has at least one conditional edge (prevents infinite loops)executor.rs). It just needs to:What the service_agent template looks like with loops:
No
retry_executionnode needed. Code_generation gets afeedbackinput that's empty on first run and populated by error_debug on retries.6. Model escalation on loop iterations
A very common pattern: start with a cheap/fast model, escalate to a more capable one on retry. For the service_agent: first attempt uses
gpt-4o-mini, after 1 failure escalate togpt-4o, after 2 failures tryclaude-sonnet.This should NOT be a special engine feature. It follows naturally from the uniform loop model:
modelfield inai_configis already a template variable ({{model}})error_debugnode can output anext_modelfield based on the iteration countcode_generation'smodelinput gets overridden by the mappednext_modelfrom error_debugmodelvalueAlternatively, the condition expression itself can drive this:
iteration < 1mapsnext_model: "gpt-4o"iteration >= 1 && iteration < 3mapsnext_model: "claude-sonnet-4-6"iteration >= 3goes to result_summary with the final errorThis keeps escalation as data flow, not engine magic.
7. Future: visual aids for loops in the editor
Once loops work at the engine level, the editor can add optional visual grouping — detecting cycles and drawing a subtle bounding box around the loop body with iteration settings shown as a badge. This is purely a UI convenience, not a model change. The underlying representation stays the same: nodes + edges + conditions.
Possible visual aids:
This is low priority — the engine and condition model come first.
Summary of changes by priority