Workflows
Workflows are composable, multi-step automation sequences that can be defined, saved, replayed, and refined. They live in the JD.AI.Workflows project and provide a structured alternative to ad-hoc agent conversations.
What workflows are
A workflow is a named sequence of steps that JD.AI executes in order. Each step can invoke a skill, call a tool, loop conditionally, or branch based on results. Workflows are:
- Replayable — run the same workflow across different projects
- Refinable — adjust parameters and re-run with
/workflow refine - Composable — nest workflows within workflows
- Cataloged — saved to
~/.jdai/workflows/for reuse
Architecture
┌──────────────────────┐
│ /workflow commands │ CLI interface
├──────────────────────┤
│ IWorkflowCatalog │ Storage (save, list, get, delete)
├──────────────────────┤
│ IWorkflowMatcher │ Match user requests to workflows
├──────────────────────┤
│ Workflow Engine │ Step execution loop
├──────────────────────┤
│ AgentSteps │ Step implementations
└──────────────────────┘
Key interfaces
public interface IWorkflowCatalog
{
Task SaveAsync(AgentWorkflowDefinition workflow, CancellationToken ct = default);
Task<AgentWorkflowDefinition?> GetAsync(string name, string? version = null, CancellationToken ct = default);
Task<IReadOnlyList<AgentWorkflowDefinition>> ListAsync(CancellationToken ct = default);
Task DeleteAsync(string name, CancellationToken ct = default);
}
public interface IWorkflowMatcher
{
Task<WorkflowMatchResult?> MatchAsync(AgentRequest request, CancellationToken ct = default);
}
public interface IAgentWorkflowDetector
{
bool IsWorkflowRequired(AgentRequest request);
}
Workflow definition
An AgentWorkflowDefinition describes a complete workflow:
public record AgentWorkflowDefinition
{
public string Name { get; init; }
public string? Version { get; init; }
public string Description { get; init; }
public IReadOnlyList<string> Tags { get; init; }
public IReadOnlyList<AgentStepDefinition> Steps { get; init; }
public DateTimeOffset CreatedAt { get; init; }
public DateTimeOffset? UpdatedAt { get; init; }
}
YAML format
Workflows can be defined in YAML and loaded by the catalog:
name: code-review-pipeline
version: "1.0"
description: Full code review with security and test coverage checks
tags: [review, security, testing]
steps:
- kind: skill
name: Analyze Code
target: code-review
parameters:
focus: security
- kind: tool
name: Run Tests
target: run_command
parameters:
command: dotnet test --verbosity minimal
- kind: conditional
name: Check Coverage
condition: "{{previous.exitCode}} == 0"
subSteps:
- kind: tool
name: Coverage Report
target: run_command
parameters:
command: dotnet test --collect:"XPlat Code Coverage"
- kind: skill
name: Synthesize Report
target: summarize
parameters:
format: markdown
Step types
Steps are defined by the AgentStepKind enum:
| Kind | Description | Builder method |
|---|---|---|
Skill |
Invoke a named skill | AgentStepDefinition.RunSkill(name) |
Tool |
Call a Semantic Kernel tool | AgentStepDefinition.InvokeTool(name) |
Nested |
Run a sub-workflow | AgentStepDefinition.RunWorkflow(name) |
Loop |
Repeat steps until a condition | AgentStepDefinition.LoopUntil(condition, subSteps) |
Conditional |
Branch based on a condition | AgentStepDefinition.If(condition, subSteps) |
Step definition
public record AgentStepDefinition
{
public AgentStepKind Kind { get; init; }
public string Name { get; init; }
public string? Target { get; init; }
public string? Condition { get; init; }
public IReadOnlyDictionary<string, string>? Parameters { get; init; }
public IReadOnlyList<AgentStepDefinition>? SubSteps { get; init; }
// Fluent builders
public static AgentStepDefinition RunSkill(string name) => ...;
public static AgentStepDefinition InvokeTool(string name) => ...;
public static AgentStepDefinition LoopUntil(string condition, params AgentStepDefinition[] subSteps) => ...;
public static AgentStepDefinition If(string condition, params AgentStepDefinition[] subSteps) => ...;
}
Workflow engine
The engine executes steps sequentially through a IWorkflowContext:
public interface IWorkflowContext<TData>
{
TData Data { get; }
IReadOnlyDictionary<string, object> StepResults { get; }
void SetStepResult(string stepName, object result);
CancellationToken CancellationToken { get; }
}
Execution flow
WorkflowEngine.RunAsync(definition)
→ foreach step in definition.Steps
→ Resolve step executor (Skill, Tool, Loop, Conditional)
→ Execute step
→ Store result in context
→ Evaluate next step conditions
→ Return workflow result
Step execution
Each step kind has an executor. For example, RunSkillStep:
public class RunSkillStep : IWorkflowStep
{
public async Task ExecuteAsync(IWorkflowContext<AgentWorkflowData> context)
{
var skillName = _stepDefinition.Target;
// Load skill, execute via agent, store result
var result = await _agentSession.RunSkillAsync(skillName, _stepDefinition.Parameters);
context.SetStepResult(_stepDefinition.Name, result);
}
}
Workflow Enforcement
The workflow enforcement layer intercepts every tool call and determines whether a structured workflow is required before the agent proceeds. It sits between the user request and the workflow engine, acting as a gatekeeper.
See Workflow Enforcement for the full DSL reference, agent roles, and enforcement modes.
In relation to the content on this page: the workflow engine (described above) executes step sequences, while the enforcement layer decides whether a workflow must be run at all and blocks raw tool calls when a matching plan-first workflow is required. The two layers work together — enforcement selects or mandates a workflow, the engine executes it.
Conversation-to-Workflow Capture
The agent can turn a completed conversation into a reusable workflow automatically. After walking through a multi-step task interactively, run:
/workflow capture
JD.AI inspects the recent conversation history, extracts the sequence of tool calls and skill invocations, and writes a new workflow YAML to ~/.jdai/workflows/. You are prompted to name the workflow and optionally edit the generated YAML before saving.
This is the fastest way to build a workflow — do it interactively once, capture it, then replay it across any project with /workflow run <name>.
Workflow Versioning
Every AgentWorkflowDefinition carries an optional Version field. When you refine an existing workflow (add steps, change conditions, update parameters), JD.AI creates a new version and archives the previous one:
~/.jdai/workflows/
code-review-pipeline/
current.yaml ← active version
versions/
1.0.yaml
1.1.yaml
2.0.yaml
Version commands:
| Command | Description |
|---|---|
/workflow history <name> |
List all saved versions with timestamps |
/workflow rollback <name> <version> |
Make an older version the active version |
Example:
> /workflow history code-review-pipeline
code-review-pipeline versions:
2.0 (current) 2026-03-01 — added blackboard aggregation step
1.1 2026-02-14 — added conditional coverage gate
1.0 2026-01-30 — initial version
> /workflow rollback code-review-pipeline 1.1
Rolled back code-review-pipeline to version 1.1
Old versions are never deleted automatically; remove them manually from ~/.jdai/workflows/<name>/versions/ if needed.
Workflow Commands Reference
| Command | Description |
|---|---|
/workflow list |
Show all saved workflows |
/workflow show <name> |
Display workflow YAML |
/workflow run <name> |
Execute a saved workflow |
/workflow create |
Wizard to create a new workflow |
/workflow capture |
Capture recent conversation as a workflow |
/workflow edit <name> |
Open workflow in editor |
/workflow delete <name> |
Remove a workflow |
/workflow history <name> |
Show version history |
/workflow rollback <name> <version> |
Revert to an earlier version |
/workflow export <name> <file> |
Export workflow to a YAML file |
/workflow import <file> |
Import workflow from a YAML file |
Example usage
> /workflow run code-review-pipeline
Running workflow: code-review-pipeline (5 steps)
✓ Step 1/5: Analyze Code (12s)
✓ Step 2/5: Run Tests (45s)
✓ Step 3/5: Check Coverage (conditionally executed)
✓ Step 4/5: Coverage Report (8s)
✓ Step 5/5: Synthesize Report (6s)
Workflow complete (1m 11s)
Writing custom workflow steps
1. Define the step class
public class HttpRequestStep : IWorkflowStep
{
private readonly AgentStepDefinition _step;
private readonly HttpClient _httpClient;
public HttpRequestStep(AgentStepDefinition step, HttpClient httpClient)
{
_step = step;
_httpClient = httpClient;
}
public async Task ExecuteAsync(IWorkflowContext<AgentWorkflowData> context)
{
var url = _step.Parameters?["url"] ?? throw new ArgumentException("url is required");
var method = _step.Parameters?.GetValueOrDefault("method", "GET");
var response = method.ToUpperInvariant() switch
{
"GET" => await _httpClient.GetStringAsync(url, context.CancellationToken),
"POST" => await PostAsync(url, _step.Parameters, context.CancellationToken),
_ => throw new ArgumentException($"Unsupported method: {method}")
};
context.SetStepResult(_step.Name, response);
}
}
2. Register the step executor
Register custom step kinds in the workflow engine's step resolver.
Testing workflows
Test workflows by constructing definitions programmatically:
[Fact]
public async Task Workflow_ExecutesStepsInOrder()
{
var definition = new AgentWorkflowDefinition
{
Name = "test-workflow",
Description = "Test",
Tags = Array.Empty<string>(),
Steps = new[]
{
AgentStepDefinition.RunSkill("analyze"),
AgentStepDefinition.InvokeTool("run_command"),
}
};
var engine = new WorkflowEngine(mockExecutor, mockCatalog);
var result = await engine.RunAsync(definition);
Assert.Equal(2, result.StepResults.Count);
}
[Fact]
public async Task ConditionalStep_SkipsWhenConditionFalse()
{
var definition = new AgentWorkflowDefinition
{
Name = "conditional-test",
Steps = new[]
{
AgentStepDefinition.If("false", AgentStepDefinition.RunSkill("should-not-run")),
}
};
var result = await engine.RunAsync(definition);
Assert.Empty(result.StepResults);
}
See also
- Architecture Overview — where workflows fit in the system
- Custom Tools — tools used by workflow steps
- Commands Reference — all slash commands