fix: allow 'always' condition on loop edges when maxIterations is set

The WorkflowValidator rejected loop edges with an 'always' condition,
but maxIterations already guarantees termination at runtime (enforced by
WorkflowConditionEvaluator). This made built-in templates like
Collaborative Group Chat fail validation immediately.

Relax the check so 'always' conditions are accepted when maxIterations
provides the termination guarantee. The condition error now only fires
when neither a non-default condition nor a maxIterations cap is present.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-04-07 10:11:49 +02:00
co-authored by Copilot
parent ecba1be92a
commit cbc05c8f08
2 changed files with 40 additions and 2 deletions
@@ -320,13 +320,14 @@ public sealed class WorkflowValidator
});
}
if (edge.Condition is null || string.Equals(edge.Condition.Type, "always", StringComparison.OrdinalIgnoreCase))
if ((edge.Condition is null || string.Equals(edge.Condition.Type, "always", StringComparison.OrdinalIgnoreCase))
&& (edge.MaxIterations is null || edge.MaxIterations < 1))
{
issues.Add(new WorkflowValidationIssueDto
{
Field = "graph.edges.condition",
EdgeId = edge.Id,
Message = "Loop edges require a non-default condition so the loop can terminate.",
Message = "Loop edges require either a non-default condition or a maxIterations cap so the loop can terminate.",
});
}
@@ -152,6 +152,43 @@ public sealed class WorkflowValidatorTests
Assert.DoesNotContain(issues, issue => issue.Level == "error");
}
[Fact]
public void Validate_AcceptsLoopWithAlwaysConditionAndMaxIterations()
{
WorkflowDefinitionDto workflow = CreateWorkflow();
workflow = new WorkflowDefinitionDto
{
Id = workflow.Id,
Name = workflow.Name,
Settings = workflow.Settings,
Graph = new WorkflowGraphDto
{
Nodes = workflow.Graph.Nodes,
Edges =
[
.. workflow.Graph.Edges,
new WorkflowEdgeDto
{
Id = "edge-loop",
Source = "agent",
Target = "agent",
Kind = "direct",
IsLoop = true,
MaxIterations = 5,
Condition = new EdgeConditionDto
{
Type = "always",
},
},
],
},
};
IReadOnlyList<WorkflowValidationIssueDto> issues = _validator.Validate(workflow);
Assert.DoesNotContain(issues, issue => issue.Level == "error");
}
[Fact]
public void Validate_AcceptsPhase4ExecutableNodeKinds()
{