From cbc05c8f0846daddc369b33835f607ceddbfcb1a Mon Sep 17 00:00:00 2001 From: David Kaya Date: Tue, 7 Apr 2026 10:11:49 +0200 Subject: [PATCH] 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> --- .../Services/WorkflowValidator.cs | 5 ++- .../WorkflowValidatorTests.cs | 37 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/sidecar/src/Aryx.AgentHost/Services/WorkflowValidator.cs b/sidecar/src/Aryx.AgentHost/Services/WorkflowValidator.cs index 11cebaa..0e76236 100644 --- a/sidecar/src/Aryx.AgentHost/Services/WorkflowValidator.cs +++ b/sidecar/src/Aryx.AgentHost/Services/WorkflowValidator.cs @@ -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.", }); } diff --git a/sidecar/tests/Aryx.AgentHost.Tests/WorkflowValidatorTests.cs b/sidecar/tests/Aryx.AgentHost.Tests/WorkflowValidatorTests.cs index d180f99..9424411 100644 --- a/sidecar/tests/Aryx.AgentHost.Tests/WorkflowValidatorTests.cs +++ b/sidecar/tests/Aryx.AgentHost.Tests/WorkflowValidatorTests.cs @@ -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 issues = _validator.Validate(workflow); + + Assert.DoesNotContain(issues, issue => issue.Level == "error"); + } + [Fact] public void Validate_AcceptsPhase4ExecutableNodeKinds() {