cloud, codes, AI

26 September 2026

Let’s create ninja! sub-agent agile team


One long chat with one AI assistant works for small tasks. It starts to break down when the work has stages: understand the idea, plan it, build it, test it. Context gets muddy, and the assistant happily skips steps.

The fix is orchestration. Give each stage to a focused agent, make each agent write a file, and let the next agent read that file. This is the core idea behind BMAD (Breakthrough Method for Agile AI Driven Development): specialized roles, written handoffs, and a repeatable flow.

In this tutorial you will build a condensed version of that idea in Visual Studio Code:

  • Four Claude Code subagents: Analyst, PM, Developer, Tester
  • Slash commands to run each stage
  • A small C# orchestrator that runs the whole flow and checks every handoff

By the end you can open one folder in VS Code, press a task, and watch an idea become a brief, a PRD, working code, and a test report.

Note. This is a lightweight take on BMAD concepts, not the official BMAD Method package. It keeps the parts that matter most: roles, artifacts, and gates.


What you will build

Idea -> Analyst -> PM -> Developer -> Tester -> Verdict
brief.md prd.md code + notes test report

Each arrow is a handoff: one agent writes a file, the next reads it.

StepAgentReadsWrites
1analystsamples/idea.mdartifacts/01-brief.md
2pmartifacts/01-brief.mdartifacts/02-prd.md
3developerartifacts/02-prd.mdworkspace/ and 03-build-notes.md
4testerPRD and codeartifacts/04-test-report.md

Prerequisites

ToolVersionCheck
Visual Studio Codecurrentcode --version
.NET SDK8.0 or newerdotnet --version
Claude Code CLIcurrentclaude --version
Gitanygit --version

You also need a Claude account that can use Claude Code, and you must be signed in (claude once in a terminal will guide you).

Install these VS Code extensions:

  • C# Dev Kit (ms-dotnettools.csdevkit)
  • C# (ms-dotnettools.csharp)
  • Claude Code for VS Code (anthropic.claude-code)

Checkpoint: all four version commands print a version number.


Step 1: Create the project structure

Create this layout (or use the ready made project that comes with this article):

bmad-lite/
CLAUDE.md
BmadLite.sln
.claude/
agents/ analyst.md pm.md developer.md tester.md
commands/ brief.md prd.md build.md test.md
settings.json
.vscode/ tasks.json launch.json extensions.json
src/BmadLite.Orchestrator/ the C# orchestrator
samples/idea.md
artifacts/ agent outputs land here
workspace/ generated code lands here

Open the folder with File > Open Folder in VS Code.

Checkpoint: the Explorer shows the .claude and src folders.

Step 2: Write the shared rules in CLAUDE.md

CLAUDE.md is loaded by Claude Code at the start of every session. Use it for rules that every agent must follow.

# BMAD Lite Project Rules
## Rules for all agents
1. Stay in your role. Do not do the job of another agent.
2. Write your output artifact to `artifacts/` using the exact file name.
3. End every artifact with a `## Handoff` section (open questions, next agent).
4. Number requirements as FR-1, FR-2 so the tester can trace them.
5. Target stack for generated code is C# on .NET 8, under `workspace/`.
6. If input is missing or unclear, say so in `## Handoff` and stop.

Rule 3 matters most. The orchestrator will later refuse to continue if an artifact has no Handoff section.

Checkpoint: CLAUDE.md exists in the project root.

Step 3: Define the four agents as subagents

Claude Code subagents are markdown files in .claude/agents/. Each has a small header (name, description, allowed tools) and a system prompt.

.claude/agents/analyst.md

---
name: analyst
description: Business analyst. Turns a raw idea into a short project brief. Use first.
tools: Read, Write, Glob, Grep
---
You are the Analyst in a condensed BMAD workflow.
Input: the idea file you are given (default `samples/idea.md`).
Output: `artifacts/01-brief.md`.
Sections: Problem, Target users, Goals, Scope (in and out),
Assumptions and risks, Handoff (next agent is `pm`).
Rules:
- Do not propose a technical design.
- Keep the brief under 60 lines.

.claude/agents/pm.md

---
name: pm
description: Product manager. Converts the brief into a PRD with numbered, testable requirements.
tools: Read, Write, Glob, Grep
---
You are the Product Manager in a condensed BMAD workflow.
Input: `artifacts/01-brief.md`. Output: `artifacts/02-prd.md`.
Sections: Summary, Functional requirements (FR-1...), Non functional
requirements (NFR-1...), Acceptance criteria (Given, When, Then),
Out of scope, Handoff (next agent is `developer`).
Rules:
- Every requirement must be testable without asking you.
- At most 8 functional requirements.

.claude/agents/developer.md

---
name: developer
description: Developer. Implements the PRD as a small C# on .NET 8 solution under workspace/.
tools: Read, Write, Edit, Glob, Grep, Bash
---
You are the Developer in a condensed BMAD workflow.
Input: `artifacts/02-prd.md`.
Output: code under `workspace/` and `artifacts/03-build-notes.md`.
Steps: implement each FR, reference the FR id in a short comment,
run `dotnet build` and fix errors, then write build notes with an
FR coverage table and a Handoff section (next agent is `tester`).
Rules:
- Never claim an FR is done if the build fails.

.claude/agents/tester.md

---
name: tester
description: Tester. Verifies the code against the PRD and reports pass or fail per requirement.
tools: Read, Write, Edit, Glob, Grep, Bash
---
You are the Tester in a condensed BMAD workflow.
Input: PRD, build notes, and code under `workspace/`.
Output: `artifacts/04-test-report.md`.
Steps: write or find automated tests for each FR and NFR, run
`dotnet test`, then report a table (requirement, test, result, evidence).
Rules:
- Do not fix production code. Report defects instead.
- Report only results you actually observed.
- End with exactly `VERDICT: PASS` or `VERDICT: FAIL`, then a Handoff section.

Two design choices make this reliable:

  1. Least privilege. The analyst and PM cannot run shell commands. Only the developer and tester can.
  2. Separation of duties. The tester cannot edit production code, so it cannot quietly “fix” a failure to get a pass.

Checkpoint: in a Claude Code session, run /agents. You should see all four agents listed.

Step 4: Add slash commands for each stage

Slash commands are markdown files in .claude/commands/. They let you run one stage on demand.

.claude/commands/brief.md

---
description: Run the analyst agent to create the project brief
argument-hint: [path to idea file]
---
Use the analyst subagent to read $ARGUMENTS (default `samples/idea.md`)
and write `artifacts/01-brief.md`. Report only the file path when done.

Create prd.md, build.md, and test.md the same way, each pointing at its own agent and artifact.

Checkpoint: type / in the Claude Code panel. You should see /brief, /prd, /build, and /test.

Step 5: Set safe permissions

Agents write files and run commands, so limit what they can do in .claude/settings.json:

{
"permissions": {
"allow": [
"Read",
"Write(artifacts/**)",
"Write(workspace/**)",
"Edit(workspace/**)",
"Bash(dotnet build:*)",
"Bash(dotnet test:*)",
"Bash(dotnet new:*)"
],
"deny": [
"Read(.env)",
"Bash(rm -rf:*)"
]
}
}

Agents can now write only to artifacts/ and workspace/, and run only .NET build, test, and new commands.

Step 6: Try the flow by hand

Before automating, prove the flow works manually. Write a small idea in samples/idea.md:

# Idea: Tip Splitter
A small .NET library and console app that splits a restaurant bill
among friends. It should handle a tip percentage, uneven shares,
and rounding so the total always matches.

Then, in the Claude Code panel, run the stages in order:

/brief samples/idea.md
/prd
/build
/test

After each command, open the new file in artifacts/ and read it. This is the human review gate that BMAD encourages: you stay in control between stages.

Checkpoint: artifacts/ contains 01-brief.md through 04-test-report.md.

Step 7: Build the C# orchestrator

Manual runs are good for learning. For repeatable runs, add a small orchestrator that calls Claude Code in non interactive print mode (claude -p) and checks each handoff.

Create the project:

dotnet new sln -n BmadLite
dotnet new console -n BmadLite.Orchestrator -o src/BmadLite.Orchestrator
dotnet sln add src/BmadLite.Orchestrator

The orchestrator has four small parts.

7.1 The workflow definition

AgentStep.cs declares each step as data: the agent, the prompt, the artifact it must produce, and the inputs it needs.

namespace BmadLite.Orchestrator;
public sealed record AgentStep(
string Agent,
string Prompt,
string OutputArtifact,
string[] RequiredInputs);
public static class Workflow
{
public static IReadOnlyList<AgentStep> Build(string ideaPath) =>
[
new("analyst",
$"Use the analyst subagent to read {ideaPath} and write artifacts/01-brief.md.",
"artifacts/01-brief.md", [ideaPath]),
new("pm",
"Use the pm subagent to read artifacts/01-brief.md and write artifacts/02-prd.md.",
"artifacts/02-prd.md", ["artifacts/01-brief.md"]),
new("developer",
"Use the developer subagent to read artifacts/02-prd.md, implement it under workspace/, and write artifacts/03-build-notes.md.",
"artifacts/03-build-notes.md", ["artifacts/02-prd.md"]),
new("tester",
"Use the tester subagent to verify workspace/ against artifacts/02-prd.md and write artifacts/04-test-report.md.",
"artifacts/04-test-report.md",
["artifacts/02-prd.md", "artifacts/03-build-notes.md"]),
];
}

Because the workflow is plain data, adding a fifth agent (for example an Architect) means adding one record and one agent file.

7.2 The Claude runner

ClaudeRunner.cs starts the claude CLI. It uses ArgumentList so you never fight shell quoting, and it supports a dry run.

using System.Diagnostics;
namespace BmadLite.Orchestrator;
public sealed record RunResult(int ExitCode, string Output, string Error);
public sealed class ClaudeRunner
{
private readonly string _workingDirectory;
private readonly bool _dryRun;
public ClaudeRunner(string workingDirectory, bool dryRun)
{
_workingDirectory = workingDirectory;
_dryRun = dryRun;
}
public async Task<RunResult> RunAsync(string prompt, CancellationToken ct = default)
{
if (_dryRun)
return new RunResult(0, $"[dry run] claude -p \"{prompt}\"", string.Empty);
var psi = new ProcessStartInfo
{
FileName = "claude",
WorkingDirectory = _workingDirectory,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
};
psi.ArgumentList.Add("-p");
psi.ArgumentList.Add(prompt);
psi.ArgumentList.Add("--permission-mode");
psi.ArgumentList.Add("acceptEdits");
try
{
using var process = Process.Start(psi)
?? throw new InvalidOperationException("Could not start claude.");
var stdout = process.StandardOutput.ReadToEndAsync(ct);
var stderr = process.StandardError.ReadToEndAsync(ct);
await process.WaitForExitAsync(ct);
return new RunResult(process.ExitCode, await stdout, await stderr);
}
catch (System.ComponentModel.Win32Exception)
{
return new RunResult(127, string.Empty,
"The 'claude' command was not found. Install Claude Code and add it to PATH.");
}
}
}

--permission-mode acceptEdits lets the agent write files without stopping to ask, while your settings.json still limits where it may write.

7.3 The handoff gate

HandoffGate.cs is the quality gate. It is the most important file in the orchestrator, because it turns “the agent said it finished” into “the artifact really exists and is well formed.”

namespace BmadLite.Orchestrator;
public static class HandoffGate
{
public static IReadOnlyList<string> CheckInputs(string root, AgentStep step)
{
var problems = new List<string>();
foreach (var input in step.RequiredInputs)
{
var path = Path.Combine(root, input);
if (!File.Exists(path)) problems.Add($"Missing input: {input}");
else if (new FileInfo(path).Length == 0) problems.Add($"Empty input: {input}");
}
return problems;
}
public static IReadOnlyList<string> CheckOutput(string root, AgentStep step)
{
var problems = new List<string>();
var path = Path.Combine(root, step.OutputArtifact);
if (!File.Exists(path))
{
problems.Add($"Missing output: {step.OutputArtifact}");
return problems;
}
var text = File.ReadAllText(path);
if (!text.Contains("## Handoff", StringComparison.OrdinalIgnoreCase))
problems.Add($"{step.OutputArtifact} has no '## Handoff' section.");
if (step.Agent == "tester" &&
!text.Contains("VERDICT: PASS") && !text.Contains("VERDICT: FAIL"))
problems.Add("Test report has no VERDICT line.");
return problems;
}
public static bool IsPass(string root, AgentStep step)
{
var path = Path.Combine(root, step.OutputArtifact);
return File.Exists(path) && File.ReadAllText(path).Contains("VERDICT: PASS");
}
}

7.4 The entry point

Program.cs parses two commands: run for the full flow and step for one agent. It finds the project root by walking up until it sees CLAUDE.md, so it works from any folder in the repo.

The core loop is short:

var steps = Workflow.Build(idea);
for (var i = 0; i < steps.Count; i++)
{
var step = steps[i];
var inputProblems = HandoffGate.CheckInputs(root, step);
if (inputProblems.Count > 0) return Fail(string.Join(Environment.NewLine, inputProblems));
var result = await runner.RunAsync(step.Prompt);
if (result.ExitCode != 0) return Fail($"{step.Agent} failed. {result.Error}");
var outputProblems = HandoffGate.CheckOutput(root, step);
if (outputProblems.Count > 0) return Fail(string.Join(Environment.NewLine, outputProblems));
Console.WriteLine($"[{i + 1}/{steps.Count}] {step.Agent} OK: {step.OutputArtifact}");
}

The full file, with the step command, usage text, and the final verdict check, is in the project download.

Checkpoint: run dotnet build BmadLite.sln. It should finish with Build succeeded.

Step 8: Wire everything into VS Code

Add tasks so you never type long commands. In .vscode/tasks.json:

{
"version": "2.0.0",
"tasks": [
{
"label": "build orchestrator",
"type": "process",
"command": "dotnet",
"args": ["build", "${workspaceFolder}/BmadLite.sln"],
"group": { "kind": "build", "isDefault": true },
"problemMatcher": "$msCompile"
},
{
"label": "dry run workflow",
"type": "process",
"command": "dotnet",
"args": ["run", "--project", "${workspaceFolder}/src/BmadLite.Orchestrator",
"--", "run", "samples/idea.md", "--dry-run"],
"options": { "cwd": "${workspaceFolder}" },
"problemMatcher": []
},
{
"label": "run full workflow",
"type": "process",
"command": "dotnet",
"args": ["run", "--project", "${workspaceFolder}/src/BmadLite.Orchestrator",
"--", "run", "samples/idea.md"],
"options": { "cwd": "${workspaceFolder}" },
"dependsOn": ["build orchestrator"],
"problemMatcher": []
}
]
}

For debugging, add .vscode/launch.json with a coreclr configuration that points at bin/Debug/net8.0/BmadLite.Orchestrator.dll and passes run samples/idea.md --dry-run. Now you can set breakpoints in the gate and step through a run with F5.

Checkpoint: press Ctrl+Shift+P, choose Tasks: Run Task, pick dry run workflow.

Step 9: Run it end to end

First, always do a dry run. It prints the exact command for each step and calls no AI:

dotnet run --project src/BmadLite.Orchestrator -- run samples/idea.md --dry-run

Expected output:

[1/4] analyst
[dry run] claude -p "Use the analyst subagent to read samples/idea.md and write artifacts/01-brief.md."
[2/4] pm
[dry run] claude -p "Use the pm subagent to read artifacts/01-brief.md and write artifacts/02-prd.md."
[3/4] developer
[dry run] claude -p "Use the developer subagent to read artifacts/02-prd.md, ..."
[4/4] tester
[dry run] claude -p "Use the tester subagent to verify workspace/ against ..."
Workflow finished.

Then run the real flow:

dotnet run --project src/BmadLite.Orchestrator -- run samples/idea.md

A real run can take several minutes because the developer and tester agents build and test code. When it ends, check:

  1. artifacts/01-brief.md to 04-test-report.md all exist.
  2. workspace/ has a solution that builds with dotnet build.
  3. The test report ends with VERDICT: PASS (or FAIL with defects listed).

If the verdict is FAIL, the orchestrator exits with code 1, which makes it easy to use in a CI pipeline later.

Troubleshooting

SymptomLikely causeFix
The 'claude' command was not foundCLI not on PATHReinstall Claude Code, restart VS Code, check claude --version
Agent is not listed in /agentsWrong folder or bad headerFiles must be in .claude/agents/ and start with the --- header
Missing output: artifacts/...Agent wrote elsewhere or was blockedCheck settings.json permissions and the agent prompt path
has no '## Handoff' sectionAgent skipped the ruleStrengthen rule 3 in CLAUDE.md and re run that step
Tester always reports PASSTester did not run testsRequire dotnet test output as evidence in the report table
Build fails at restore on a locked down networkNuGet is blockedUse an internal NuGet mirror or an offline package cache

Best practices

  • Review between stages the first few times. Automate only after you trust the artifacts.
  • Keep artifacts short. Small files are cheaper to read and easier for the next agent to use.
  • Keep the tester independent. Never let the same agent write and grade the code.
  • Commit the artifacts to Git. They become a readable history of decisions.
  • Version your agents. Treat .claude/agents/*.md like code and review changes in pull requests.

Where to go next

  • Add an Architect agent between PM and Developer for design decisions.
  • Add a retry loop: if the verdict is FAIL, send the defects back to the developer once.
  • Run the orchestrator in GitHub Actions or Azure Pipelines for repeatable builds.
  • Move from the CLI to a code first framework such as Semantic Kernel or Microsoft Agent Framework if you need finer control over tools and state.

Summary

You built agent orchestration with three simple ideas: specialized roles (.claude/agents), written handoffs (artifacts/), and enforced gates (the C# orchestrator). Claude Code does the reasoning, and your C# code keeps the process honest. The same pattern scales from a tip splitter to a real product backlog. Ah you can download the sample codes here https://github.com/ridiferd/leanbmad


Discussion

Leave a Reply

Discover more from ridilabs

Subscribe now to keep reading and get access to the full archive.

Continue reading