# Maf Workflow Smoke Tester

> Generates structural smoke tests for MAF 1.3.0 workflow patterns. Five categories: fan-out/fan-in completeness, session round-trip, streaming, structured output, tool invocation. Catches the silent runtime failures static analysis cannot.

- **Type:** Skill
- **Install:** `agentstack add skill-joslat-maf-doctor-maf-workflow-smoke-tester`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [joslat](https://agentstack.voostack.com/s/joslat)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [joslat](https://github.com/joslat)
- **Source:** https://github.com/joslat/maf-doctor/tree/main/.github/skills/maf-workflow-smoke-tester

## Install

```sh
agentstack add skill-joslat-maf-doctor-maf-workflow-smoke-tester
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# workflow-smoke-tester

## Purpose

`dotnet build` passing does not mean the code works. The fan-in starvation bug is the canonical example: builds green, runs silently wrong, no error. Smoke tests add a fast, credential-free runtime check for the class of failures that static analysis alone cannot catch.

These tests use **mock chat clients** — no Azure credentials required, no network calls. They verify structural correctness, not AI output quality.

## ⚡ The fastest path — use the MCP tools

The maf-autopilot MCP server now covers most of the smoke-test surface automatically. Prefer these:

| What you want to verify                              | Use this tool                                |
|------------------------------------------------------|----------------------------------------------|
| Every fan-out `[MessageHandler]` returns `Task`   | `MafValidateFanOut(repoPath)`                |
| Workflow topology can complete (no silent starvation) | `MafSimulateWorkflow(repoPath)`             |
| New agent/executor scaffold ships with smoke test    | `MafNewAgent` / `MafNewExecutor`             |
| Agent prompt quality (injection, refusals, bloat)    | `MafLintAgentPrompt(repoPath)`               |
| Single-command health letter                         | `MafDoctor(repoPath)` — covers all of above  |

When you call `MafNewExecutor`, the generated `*Tests.cs` already includes the reflection-based "fan-out handler returns `Task`" structural assertion — that's the canonical smoke-test pattern. It's compile-validated against `AntiPatternScannerTool` and `FanOutValidatorTool` by the project's own dogfood test suite.

## When to use the manual templates below

Reach for them when:
- The MCP tools are unavailable.
- The codebase needs one of the patterns the tools don't yet cover (session round-trip, streaming, structured output, tool approval).
- You want to drop a snippet directly into a PR review.

## Pattern detection

```powershell
# Fan-out/fan-in pattern
Select-String -Path "src/**/*.cs" -Pattern "AddFanOutEdge" -Recurse -List

# Streaming
Select-String -Path "src/**/*.cs" -Pattern "RunStreamingAsync" -Recurse -List

# Structured output
Select-String -Path "src/**/*.cs" -Pattern "RunAsync ⚠️ **Templates below are illustrative.** Only Template 1 (fan-out shape) is compile-validated automatically — the rest are reference snippets you must adapt to your project's exact types. If a template fails to compile against a future MAF minor, file a registry-suggestion issue.

### Template 1 — Fan-out / fan-in structural smoke test (compile-validated equivalent: `MafNewExecutor` output)

```csharp
[Fact]
public void FanOutHandlers_AllReturnGenericTask()
{
    // Catches the silent-starvation pattern at build time via reflection.
    // For full coverage, also run `MafValidateFanOut(repoPath)`.
    var executorTypes = typeof(MyWorkflowMarker).Assembly.GetTypes()
        .Where(t => typeof(Executor).IsAssignableFrom(t))
        .ToList();

    foreach (var t in executorTypes)
    {
        foreach (var method in t.GetMethods(BindingFlags.Public | BindingFlags.Instance))
        {
            if (method.GetCustomAttribute() is null) continue;
            var name = method.ReturnType.Name;
            Assert.True(
                name.StartsWith("Task`") || name.StartsWith("ValueTask`")
                || name.StartsWith("IAsyncEnumerable`"),
                $"{t.Name}.{method.Name} returns {method.ReturnType.FullName} — fan-out handlers must return Task / ValueTask / IAsyncEnumerable.");
        }
    }
}
```

### Template 2 — Session round-trip

```csharp
[Fact]
public async Task Session_SerialisesAndRehydratesAsync()
{
    var agent = BuildAgentWithMockClient();
    var session = await agent.CreateSessionAsync();
    var blob = await agent.SerializeSessionAsync(session);
    Assert.False(string.IsNullOrEmpty(blob));
    var restored = await agent.DeserializeSessionAsync(blob);
    Assert.NotNull(restored);
}
```

### Template 3 — Streaming smoke test

```csharp
[Fact]
public async Task RunStreamingAsync_YieldsAtLeastOneUpdate()
{
    var agent = BuildAgentWithMockClient();
    var updates = new List();
    await foreach (var u in agent.RunStreamingAsync("hello"))
        updates.Add(u);
    Assert.NotEmpty(updates);
}
```

### Template 4 — Structured output

```csharp
[Fact]
public async Task RunAsyncT_ReturnsTypedResult()
{
    var agent = BuildAgentWithMockClient();
    var resp = await agent.RunAsync("query");
    Assert.NotNull(resp);
    Assert.NotNull(resp.Result);
}
```

### Template 5 — Tool approval flow

```csharp
[Fact]
public async Task ToolApproval_FlowsToApprovalContent()
{
    var agent = BuildAgentWithMockClient(toolRequiresApproval: true);
    var response = await agent.RunAsync("call the tool");
    var approvalRequest = response.Messages
        .SelectMany(m => m.Contents)
        .OfType()
        .FirstOrDefault();
    Assert.NotNull(approvalRequest);
}
```

## Wiring tips

- All templates assume an `IChatClient` mock — no Azure credentials needed. Reuse the `ScriptedChatClient` pattern emitted by `MafNewAgent`.
- Mark the test class `[Trait("Category", "Smoke")]` so CI can run smoke tests separately from end-to-end tests.
- Smoke tests should complete in &lt; 1 second each — they're structural, not behavioural.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [joslat](https://github.com/joslat)
- **Source:** [joslat/maf-doctor](https://github.com/joslat/maf-doctor)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-joslat-maf-doctor-maf-workflow-smoke-tester
- Seller: https://agentstack.voostack.com/s/joslat
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
