# Roslyn Source Generator for Workflow Executor Routes

## Overview

Replace the reflection-based `ReflectingExecutor<T>` pattern with a compile-time source generator that discovers `[MessageHandler]` attributed methods and generates `ConfigureRoutes`, `ConfigureSentTypes`, and `ConfigureYieldTypes` implementations.

## Design Decisions (Confirmed)

- **Attribute syntax**: Inline properties on `[MessageHandler(Yield=[...], Send=[...])]`
- **Class-level attributes**: Generate `ConfigureSentTypes()`/`ConfigureYieldTypes()` from `[SendsMessage]`/`[YieldsMessage]`
- **Migration**: Clean break - requires direct `Executor` inheritance (not `ReflectingExecutor<T>`)
- **Handler accessibility**: Any (private, protected, internal, public)

---

## Implementation Steps

### Phase 1: Create Source Generator Project

**1.1 Create project structure:**
```
dotnet/src/Microsoft.Agents.AI.Workflows.Generators/
├── Microsoft.Agents.AI.Workflows.Generators.csproj
├── ExecutorRouteGenerator.cs          # Main incremental generator
├── Models/
│   ├── ExecutorInfo.cs                 # Data model for executor analysis
│   └── HandlerInfo.cs                  # Data model for handler methods
├── Analysis/
│   ├── SyntaxDetector.cs               # Syntax-based candidate detection
│   └── SemanticAnalyzer.cs             # Semantic model analysis
├── Generation/
│   └── SourceBuilder.cs                # Code generation logic
└── Diagnostics/
    └── DiagnosticDescriptors.cs        # Analyzer diagnostics
```

**1.2 Project file configuration:**
- Target `netstandard2.0`
- Reference `Microsoft.CodeAnalysis.CSharp` 4.8.0+
- Set `IsRoslynComponent=true`, `EnforceExtendedAnalyzerRules=true`
- Package as analyzer in `analyzers/dotnet/cs`

### Phase 2: Define Attributes

**2.1 Create `MessageHandlerAttribute`:**
```
dotnet/src/Microsoft.Agents.AI.Workflows/Attributes/MessageHandlerAttribute.cs
```
```csharp
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public sealed class MessageHandlerAttribute : Attribute
{
    public Type[]? Yield { get; set; }  // Types yielded as workflow outputs
    public Type[]? Send { get; set; }   // Types sent to other executors
}
```

**2.2 Create `SendsMessageAttribute`:**
```
dotnet/src/Microsoft.Agents.AI.Workflows/Attributes/SendsMessageAttribute.cs
```
```csharp
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class SendsMessageAttribute : Attribute
{
    public Type Type { get; }
    public SendsMessageAttribute(Type type) => this.Type = type;
}
```

**2.3 Create `YieldsMessageAttribute`:**
```
dotnet/src/Microsoft.Agents.AI.Workflows/Attributes/YieldsMessageAttribute.cs
```
```csharp
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class YieldsMessageAttribute : Attribute
{
    public Type Type { get; }
    public YieldsMessageAttribute(Type type) => this.Type = type;
}
```

### Phase 3: Implement Source Generator

**3.1 Detection criteria (syntax level):**
- Class has `partial` modifier
- Class has at least one method with `[MessageHandler]` attribute

**3.2 Validation criteria (semantic level):**
- Class derives from `Executor` (directly or transitively)
- Class does NOT already define `ConfigureRoutes` with a body
- Handler method has valid signature: `(TMessage, IWorkflowContext[, CancellationToken])`
- Handler returns `void`, `ValueTask`, or `ValueTask<T>`

**3.3 Handler signature mapping:**

| Method Signature | Generated AddHandler Call |
|-----------------|---------------------------|
| `void Handler(T, IWorkflowContext)` | `AddHandler<T>(this.Handler)` |
| `void Handler(T, IWorkflowContext, CT)` | `AddHandler<T>(this.Handler)` |
| `ValueTask Handler(T, IWorkflowContext)` | `AddHandler<T>(this.Handler)` |
| `ValueTask Handler(T, IWorkflowContext, CT)` | `AddHandler<T>(this.Handler)` |
| `TResult Handler(T, IWorkflowContext)` | `AddHandler<T, TResult>(this.Handler)` |
| `ValueTask<TResult> Handler(T, IWorkflowContext, CT)` | `AddHandler<T, TResult>(this.Handler)` |

**3.4 Generated code structure:**
```csharp
// <auto-generated/>
#nullable enable

namespace MyNamespace;

partial class MyExecutor
{
    protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder)
    {
        // Call base if inheriting from another executor with routes
        // routeBuilder = base.ConfigureRoutes(routeBuilder);

        return routeBuilder
            .AddHandler<InputType1, OutputType1>(this.Handler1)
            .AddHandler<InputType2>(this.Handler2);
    }

    protected override ISet<Type> ConfigureSentTypes()
    {
        var types = base.ConfigureSentTypes();
        types.Add(typeof(SentType1));
        return types;
    }

    protected override ISet<Type> ConfigureYieldTypes()
    {
        var types = base.ConfigureYieldTypes();
        types.Add(typeof(YieldType1));
        return types;
    }
}
```

**3.5 Inheritance handling:**

| Scenario | Generated `ConfigureRoutes` |
|----------|----------------------------|
| Directly extends `Executor` | No base call (abstract) |
| Extends executor with `[MessageHandler]` methods | `routeBuilder = base.ConfigureRoutes(routeBuilder);` |
| Extends executor with manual `ConfigureRoutes` | `routeBuilder = base.ConfigureRoutes(routeBuilder);` |

### Phase 4: Analyzer Diagnostics

| ID | Severity | Condition |
|----|----------|-----------|
| `WFGEN001` | Error | Handler missing `IWorkflowContext` parameter |
| `WFGEN002` | Error | Handler has invalid return type |
| `WFGEN003` | Error | Executor with `[MessageHandler]` must be `partial` |
| `WFGEN004` | Warning | `[MessageHandler]` on non-Executor class |
| `WFGEN005` | Error | Handler has fewer than 2 parameters |
| `WFGEN006` | Info | `ConfigureRoutes` already defined, handlers ignored |

### Phase 5: Integration & Migration

**5.1 Wire generator to main project:**
```xml
<!-- Microsoft.Agents.AI.Workflows.csproj -->
<ItemGroup>
  <ProjectReference Include="..\Microsoft.Agents.AI.Workflows.Generators\..."
                    OutputItemType="Analyzer"
                    ReferenceOutputAssembly="false" />
</ItemGroup>
```

**5.2 Mark `ReflectingExecutor<T>` obsolete:**
```csharp
[Obsolete("Use [MessageHandler] attribute on methods in a partial class deriving from Executor. " +
          "See migration guide. This type will be removed in v1.0.", error: false)]
public class ReflectingExecutor<TExecutor> : Executor ...
```

**5.3 Mark `IMessageHandler<T>` interfaces obsolete:**
```csharp
[Obsolete("Use [MessageHandler] attribute instead.")]
public interface IMessageHandler<TMessage> { ... }
```

### Phase 6: Testing

**6.1 Generator unit tests:**
```
dotnet/tests/Microsoft.Agents.AI.Workflows.Generators.UnitTests/
├── ExecutorRouteGeneratorTests.cs
├── SyntaxDetectorTests.cs
├── SemanticAnalyzerTests.cs
└── TestHelpers/
    └── GeneratorTestHelper.cs
```

Test cases:
- Simple single handler
- Multiple handlers on one class
- Handlers with different signatures (void, ValueTask, ValueTask<T>)
- Nested classes
- Generic executors
- Inheritance chains (Executor -> CustomBase -> Concrete)
- Class-level `[SendsMessage]`/`[YieldsMessage]` attributes
- Manual `ConfigureRoutes` present (should skip generation)
- Invalid signatures (should produce diagnostics)

**6.2 Integration tests:**
- Port existing `ReflectingExecutor` test cases to use `[MessageHandler]`
- Verify generated routes match reflection-discovered routes

---

## Files to Create

| Path | Purpose |
|------|---------|
| `dotnet/src/Microsoft.Agents.AI.Workflows.Generators/Microsoft.Agents.AI.Workflows.Generators.csproj` | Generator project |
| `dotnet/src/Microsoft.Agents.AI.Workflows.Generators/ExecutorRouteGenerator.cs` | Main generator |
| `dotnet/src/Microsoft.Agents.AI.Workflows.Generators/Models/ExecutorInfo.cs` | Data model |
| `dotnet/src/Microsoft.Agents.AI.Workflows.Generators/Models/HandlerInfo.cs` | Data model |
| `dotnet/src/Microsoft.Agents.AI.Workflows.Generators/Analysis/SyntaxDetector.cs` | Syntax analysis |
| `dotnet/src/Microsoft.Agents.AI.Workflows.Generators/Analysis/SemanticAnalyzer.cs` | Semantic analysis |
| `dotnet/src/Microsoft.Agents.AI.Workflows.Generators/Generation/SourceBuilder.cs` | Code gen |
| `dotnet/src/Microsoft.Agents.AI.Workflows.Generators/Diagnostics/DiagnosticDescriptors.cs` | Diagnostics |
| `dotnet/src/Microsoft.Agents.AI.Workflows/Attributes/MessageHandlerAttribute.cs` | Handler attribute |
| `dotnet/src/Microsoft.Agents.AI.Workflows/Attributes/SendsMessageAttribute.cs` | Class-level send |
| `dotnet/src/Microsoft.Agents.AI.Workflows/Attributes/YieldsMessageAttribute.cs` | Class-level yield |
| `dotnet/tests/Microsoft.Agents.AI.Workflows.Generators.UnitTests/*.cs` | Generator tests |

## Files to Modify

| Path | Changes |
|------|---------|
| `dotnet/src/Microsoft.Agents.AI.Workflows/Microsoft.Agents.AI.Workflows.csproj` | Add generator reference |
| `dotnet/src/Microsoft.Agents.AI.Workflows/Reflection/ReflectingExecutor.cs` | Add `[Obsolete]` |
| `dotnet/src/Microsoft.Agents.AI.Workflows/Reflection/IMessageHandler.cs` | Add `[Obsolete]` |
| `dotnet/Microsoft.Agents.sln` | Add new projects |

---

## Example Usage (End State)

```csharp
[SendsMessage(typeof(PollToken))]
public partial class MyChatExecutor : ChatProtocolExecutor
{
    [MessageHandler]
    private async ValueTask<ChatResponse> HandleQueryAsync(
        ChatQuery query, IWorkflowContext ctx, CancellationToken ct)
    {
        // Return type automatically inferred as output
        return new ChatResponse(...);
    }

    [MessageHandler(Yield = [typeof(StreamChunk)], Send = [typeof(InternalMessage)])]
    private void HandleStream(StreamRequest req, IWorkflowContext ctx)
    {
        // Explicit Yield/Send for complex handlers
    }
}
```

Generated:
```csharp
partial class MyChatExecutor
{
    protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder)
    {
        routeBuilder = base.ConfigureRoutes(routeBuilder);
        return routeBuilder
            .AddHandler<ChatQuery, ChatResponse>(this.HandleQueryAsync)
            .AddHandler<StreamRequest>(this.HandleStream);
    }

    protected override ISet<Type> ConfigureSentTypes()
    {
        var types = base.ConfigureSentTypes();
        types.Add(typeof(PollToken));
        types.Add(typeof(InternalMessage));  // From handler attribute
        return types;
    }

    protected override ISet<Type> ConfigureYieldTypes()
    {
        var types = base.ConfigureYieldTypes();
        types.Add(typeof(ChatResponse));     // From return type
        types.Add(typeof(StreamChunk));      // From handler attribute
        return types;
    }
}
```
