Install
$ agentstack add skill-joslat-maf-doctor-maf-fan-out-validator ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
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:
TaskValueTaskIAsyncEnumerable(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 | ❌ SILENTSTARVATIONRISK | No completion signal AND no message | | Task, ValueTask (non-generic) | ❌ SILENTSTARVATIONRISK | 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:
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
- Source: joslat/maf-doctor
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.