Skip to content

AGWF / AGSR — Workflow & State Reducer Diagnostics

The AGWF (workflow) and AGSR (state reducer) diagnostics are emitted by the Strategos workflow source generator at compile time. They catch workflow definition errors before runtime — empty names, missing entry points, fork/join mismatches, and invalid reducer attribute targets. The AONT-prefixed diagnostics on the sibling pages cover the ontology layer; this page covers the workflow analyzer.

PrefixCategoryDescription
AGWFWorkflowWorkflow definition validation
AGSRState ReducerState reducer attribute validation
CodeSeverityDescription
AGWF001ErrorWorkflow name cannot be empty
AGWF002WarningNo steps found in workflow
AGWF003ErrorDuplicate step name
AGWF004ErrorInvalid namespace (global namespace not supported)
AGWF009ErrorWorkflow must begin with StartWith<T>()
AGWF010WarningWorkflow should end with Finally<T>()
AGWF012ErrorFork must be followed by Join<T>()
AGWF014ErrorLoop body cannot be empty
AGSR001Error[Append] can only be applied to collection types
AGSR002Error[Merge] can only be applied to dictionary types

PropertyValue
SeverityError
CategoryDSL Completeness

The [Workflow] attribute requires a non-empty workflow name.

Invalid:

[Workflow("")] // Error: Empty name
[Workflow(" ")] // Error: Whitespace only

Valid:

[Workflow("process-order")]

PropertyValue
SeverityWarning
CategoryDSL Completeness

The workflow definition doesn’t contain any recognizable step methods.

Trigger:

[Workflow("empty-workflow")]
public static partial class EmptyWorkflow
{
public static WorkflowDefinition<State> Definition =>
Workflow<State>.Create("empty-workflow"); // Warning: No steps
}

Resolution:

Workflow<State>.Create("my-workflow")
.StartWith<FirstStep>()
.Then<SecondStep>()
.Finally<LastStep>();

PropertyValue
SeverityError
CategoryStructural Validation

The same step type appears multiple times where each step must be unique (linear flow, fork paths).

Invalid:

.StartWith<ValidateStep>()
.Then<ProcessStep>()
.Then<ValidateStep>() // Error: Duplicate

Resolution: Use instance names to disambiguate:

.StartWith<ValidateStep>()
.Then<ProcessStep>()
.Then<ValidateStep>("FinalValidation") // OK: Different instance

::: info Duplicates in mutually exclusive branch paths are allowed since only one path executes. :::


PropertyValue
SeverityError
CategoryDSL Completeness

Workflows must be declared in a namespace. Global namespace is not supported.

Invalid:

// No namespace declaration
[Workflow("orphan-workflow")]
public static partial class OrphanWorkflow { } // Error

Valid:

namespace MyApp.Workflows;
[Workflow("my-workflow")]
public static partial class MyWorkflow { }

PropertyValue
SeverityError
CategoryDSL Completeness

Every workflow must begin with StartWith<T>() to define the entry point.

Invalid:

Workflow<State>.Create("bad-workflow")
.Then<FirstStep>() // Error: Should be StartWith
.Finally<LastStep>();

Valid:

Workflow<State>.Create("good-workflow")
.StartWith<FirstStep>()
.Finally<LastStep>();

PropertyValue
SeverityWarning
CategoryDSL Completeness

Workflows should end with Finally<T>() to mark completion.

Trigger:

Workflow<State>.Create("incomplete-workflow")
.StartWith<FirstStep>()
.Then<SecondStep>(); // Warning: No Finally

Resolution:

Workflow<State>.Create("complete-workflow")
.StartWith<FirstStep>()
.Then<SecondStep>()
.Finally<CompletionStep>();

::: info Why Warning (not Error) Some patterns intentionally short-circuit via Complete() in branches. The DSL allows this, so we warn rather than block. :::


PropertyValue
SeverityError
CategoryStructural Validation

Every Fork() construct must be followed by Join<T>() to merge parallel paths.

Invalid:

.StartWith<PrepareStep>()
.Fork(
path => path.Then<PathA>(),
path => path.Then<PathB>())
.Then<NextStep>() // Error: Fork not followed by Join
.Finally<EndStep>();

Valid:

.StartWith<PrepareStep>()
.Fork(
path => path.Then<PathA>(),
path => path.Then<PathB>())
.Join<MergeResultsStep>() // Correct: Join after Fork
.Finally<EndStep>();

PropertyValue
SeverityError
CategoryStructural Validation

RepeatUntil loops must contain at least one step in their body.

Invalid:

.StartWith<InitStep>()
.RepeatUntil(s => s.Done, "process", loop => { }) // Error: Empty body
.Finally<EndStep>();

Valid:

.StartWith<InitStep>()
.RepeatUntil(s => s.Done, "process", loop => loop
.Then<ProcessItemStep>()
.Then<CheckProgressStep>())
.Finally<EndStep>();

PropertyValue
SeverityError
CategoryState Reducer

The [Append] attribute can only be applied to collection types (e.g., List<T>, IList<T>).

Invalid:

[WorkflowState]
public record State
{
[Append]
public string Name { get; init; } // Error: Not a collection
}

Valid:

[WorkflowState]
public record State
{
[Append]
public List<string> Items { get; init; }
}

PropertyValue
SeverityError
CategoryState Reducer

The [Merge] attribute can only be applied to dictionary types (e.g., Dictionary<TKey, TValue>).

Invalid:

[WorkflowState]
public record State
{
[Merge]
public List<string> Items { get; init; } // Error: Not a dictionary
}

Valid:

[WorkflowState]
public record State
{
[Merge]
public Dictionary<string, int> Scores { get; init; }
}

Ensures the workflow definition has all required components:

  • Valid name (AGWF001)
  • Proper namespace (AGWF004)
  • Entry point via StartWith (AGWF009)
  • Exit point via Finally (AGWF010)
  • At least one step (AGWF002)

Validates the logical structure of the workflow:

  • No ambiguous step references (AGWF003)
  • Proper construct pairing like Fork/Join (AGWF012)
  • Non-empty constructs like loop bodies (AGWF014)

Validates state attribute usage:

  • [Append] on collections (AGSR001)
  • [Merge] on dictionaries (AGSR002)

SeverityMeaningCode Generation
ErrorWorkflow cannot execute correctlyBlocked
WarningPattern may be intentional but warrants reviewAllowed