# Maf Fan Out Validator

> Validates fan-out / fan-in workflow topology — detects the silent fan-in starvation pattern (handlers returning void / non-generic Task that produce no downstream message). Canonical rules + procedure for what the compiler cannot catch and the build cannot surface.

- **Type:** Skill
- **Install:** `agentstack add skill-joslat-maf-doctor-maf-fan-out-validator`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [joslat](https://agentstack.voostack.com/s/joslat)
- **Installs:** 0
- **Category:** [Developer Tools](https://agentstack.voostack.com/c/developer-tools)
- **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-fan-out-validator

## Install

```sh
agentstack add skill-joslat-maf-doctor-maf-fan-out-validator
```

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

## About

# fan-out-validator — silent fan-in starvation detection

> **⚡ Prefer the MCP tools.** The `maf-autopilot` MCP server exposes:
> - **`MafValidateFanOut(repoPath)`** — Roslyn syntax walk; flags any `[MessageHandler]` returning `void` / non-generic `Task` / `ValueTask` (the silent-starvation patterns).
> - **`MafSimulateWorkflow(repoPath)`** — builds the full executor topology graph (fan-out + fan-in edges resolved through variable bindings), emits a Mermaid diagram, and gives an end-to-end completion forecast.
>
> Use both: `MafValidateFanOut` for per-handler verdicts, `MafSimulateWorkflow` for "would this workflow complete?"
>
> *This skill formerly had a sibling `fan-in-static-analyzer` that walked the same patterns by hand; both have been merged here now that the MCP tools cover the procedural work.*

## The bug class — "silent fan-in starvation"

A fan-out executor's `[MessageHandler]` method MUST return one of:
- `Task`
- `ValueTask`
- `IAsyncEnumerable` (streaming)

A handler that returns `void`, `Task`, or `ValueTask` (non-generic) produces **no output message**. The fan-in barrier on the downstream edge then **starves silently** — the workflow exits cleanly but incompletely.

**This is NOT a build error.** `dotnet build` is clean. Tests that don't exercise the fan-in path are clean. The bug only surfaces in production traces (or with one of the tools above).

## Return-type verdict table

| Return type                              | Verdict                  | Why                                       |
|------------------------------------------|--------------------------|-------------------------------------------|
| `Task`, `ValueTask`  | ✅ OK                    | Produces a downstream message             |
| `IAsyncEnumerable`             | ✅ OK (streaming pattern)| Produces a stream of messages             |
| `void`                                   | ❌ SILENT_STARVATION_RISK | No completion signal AND no message      |
| `Task`, `ValueTask` (non-generic)        | ❌ SILENT_STARVATION_RISK | Completes but emits nothing               |
| `int`, `string`, raw concrete types      | ⚠ LIKELY_INVALID         | Cannot satisfy MAF's fan-out contract    |

## Fan-in argument-order rule

The canonical 1.3.0 form of `AddFanInBarrierEdge` is:

```csharp
builder.AddFanInBarrierEdge(
    new[] { sourceA, sourceB, sourceC },   // sources first (IEnumerable)
    target);                               // target last (single ExecutorBinding)
```

The legacy `(target, sources)` overload is `[Obsolete]` in 1.3.0 — triggers `CS0618`. The compiler catches that one. (See registry entry `MAF130-FAN-IN-001`.)

## Manual fallback

When the MCP tools are unavailable, walk every `MethodDeclarationSyntax` in the codebase that has `[MessageHandler]` and inspect its `ReturnType`. Apply the verdict table above. Cross-reference each fan-out edge against the producing executor's handler.

## Companion analyzer (write-time enforcement)

The `maf-doctor.Analyzers` NuGet package ships **`MAF001`** (Error severity) for exactly this pattern — `[MessageHandler]` returning a non-generic awaitable. Add `` to your project to catch this at write-time, before commit.

## 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-fan-out-validator
- 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%.
