DEV Community

Ali Suleyman TOPUZ
Ali Suleyman TOPUZ

Posted on • Originally published at topuzas.Medium on

From Prompt Engineering to Harness Engineering: How .NET Teams Should Build Around Claude Code

For a long time my mental model for getting good output from Claude Code was: write a better prompt. Add more context to the system prompt. Explain the convention one more time, in slightly clearer language. It worked, right up until it didn’t, and the failure mode was always the same: the agent would nail a task perfectly on Monday and quietly violate a module boundary on Tuesday, because nothing in the environment actually enforced the boundary. The prompt was a suggestion. It was never a constraint.

That’s the month I stopped thinking about prompts and started thinking about the harness. If you haven’t run into the term yet, you will soon, it’s becoming the load-bearing vocabulary for anyone building serious agent workflows in 2026. This piece is my attempt to lay out what it actually means, and what building one looks like specifically for a .NET team running Claude Code, because almost nobody has written that part down yet.

What a harness actually is

Birgitta Böckeler at Thoughtworks published the article that gave me the clearest vocabulary for this, and I’ve been borrowing her framing ever since. The shorthand is: agent = model + harness. The harness is everything around the model that isn’t the model itself, the tool schemas, the memory policy, the retrieval strategy, the sandbox configuration, the verification checks, the permission tiers, the human-review gates.

Her framework splits harness controls into two directions and two execution types:

HARNESS ENGINEERING: THE FOUR-QUADRANT MODEL
(Böckeler / Martin Fowler, "Harness engineering for coding agent users")

                    Computational (deterministic, Inferential (semantic,
                    fast, CPU) slower, LLM-judged)
--------------------------------------------------------------------------------
Feedforward Codemods, bootstrap scripts, AGENTS.md, coding
(steer BEFORE LSP-backed context, convention docs,
the agent acts) project templates Skills, spec files

Feedback Linters, type checkers, AI code review agents,
(correct AFTER ArchUnit-style structural "LLM as judge" for
the agent acts) tests, mutation testing semantic duplication
--------------------------------------------------------------------------------
Enter fullscreen mode Exit fullscreen mode

The insight that actually changed how I work is the distinction between guides (feedforward, they try to prevent bad output before it happens) and sensors (feedback, they catch it after and hand the agent a signal it can self-correct on). A harness that’s all feedforward encodes rules nobody verifies. A harness that’s all feedback means the agent keeps making the same mistake forever because nothing steered it away from the mistake in the first place. You need both, and the computational half of each is the part most .NET teams already have lying around, unconnected to their agent.

Microsoft built the .NET version of this for you

If you’re on .NET, you don’t have to invent the plumbing from scratch. Microsoft shipped the Agent Framework Harness on July 22, 2026, batteries-included, for both .NET and Python. The framework’s own description of what a harness contains lines up almost exactly with Böckeler’s model: the tool-calling loop, planning, persistent memory, context-window management, approval rules, and telemetry, fully customizable and aimed specifically at long-running autonomous tasks.

This matters for a specific reason if you’re building internal tools on top of Claude or any other model from C#: you no longer have to hand-roll the orchestration loop, memory persistence, and approval gating every time you want an agent doing real work against your codebase or your business systems. That scaffolding is now a first-class, GA, open-source .NET citizen. If your team’s agent work has been living in ad-hoc scripts calling a chat completion API in a loop, this is the point where that stops being defensible.

Give Claude Code .NET-specific sensors, not just .NET-specific prompts

Here’s where most teams stop: they write a good AGENTS.md, describe their conventions in detail, and call it done. That's a feedforward guide, and it's inferential, meaning the model has to interpret and remember it correctly every single time. It's the weakest link in the four-quadrant grid above.

dotnet-claude-kit is the clearest example I've found of someone actually building out the computational half for .NET. It's a free, MIT-licensed Claude Code plugin: 47 skills, 10 specialist agents, 16 slash commands, and, critically, 20 Roslyn-powered MCP tools that give Claude Code compiler-accurate answers about your codebase instead of pattern-matched guesses. That last part is the difference between a guide and a sensor. A skill telling Claude "we use the repository pattern" is feedforward and inferential. A Roslyn MCP tool that can actually answer "does this class implement IRepository correctly" is feedback, computational, and doesn't care how well the model remembered your conventions.

Installing it is a two-step process:

# Install the Roslyn-powered MCP server (the computational sensor layer)
dotnet tool install -g CWM.RoslynNavigator

# Inside Claude Code, add the marketplace and install the kit
/plugin marketplace add codewithmukesh/dotnet-claude-kit
/plugin install dotnet-claude-kit
Enter fullscreen mode Exit fullscreen mode

Or, if you’d rather not depend on a marketplace, it’s fully open source, so the self-hosted path is just as viable:

git clone https://github.com/codewithmukesh/dotnet-claude-kit.git
claude --plugin-dir ./dotnet-claude-kit
Enter fullscreen mode Exit fullscreen mode

Either way, once it’s loaded you have Roslyn-accurate structural checks running as an actual sensor Claude Code can query mid-task, not a paragraph of prose it’s hoping to recall correctly three tool calls later.

Memory is opt-in, and it doesn’t share across subagents by default

If your harness uses Claude Code subagents to isolate context for different parts of a .NET solution (one subagent for the API layer, one for the domain model, one for infrastructure), there’s a gap worth knowing about before you build around it. Memory for a subagent is opt-in via the frontmatter memory field, scoped to user, project, or local:

---
name: domain-model-reviewer
description: Reviews changes to the domain model against DDD conventions
memory: project
tools: [Read, Grep, Glob]
---

You review changes to files under src/Domain/** against our aggregate
boundary rules. Flag any direct reference from an aggregate root to
another aggregate's internal entities.
Enter fullscreen mode Exit fullscreen mode

Setting memory: project gives this subagent a persistent directory where it accumulates what it learns across invocations, useful for building up a running list of the boundary violations it's caught before. What it doesn't do is share that memory with your other subagents. Every subagent invocation starts fresh relative to its siblings; whatever the API-layer subagent figured out about a shared convention stays siloed from the infrastructure subagent unless you explicitly pipe it through a shared file both of them read. If you're designing a multi-subagent harness for a .NET solution with real module boundaries, plan the shared-knowledge path deliberately, don't assume memory composes on its own.

Background subagents add one more wrinkle worth knowing: they auto-deny any tool call outside what was pre-approved when they were launched. If a background subagent hits an unapproved tool mid-task, that specific call fails but the subagent keeps running, it doesn’t halt the whole task. That’s a sensible default for long-running background work, but it means a background subagent can silently skip a step it needed, which loops back to the verification-gap problem: check what it actually did, not just whether it finished.

The spec is the feedforward layer for behavior, and it’s the hardest one

Böckeler’s framework calls out “behavior harness,” making sure the agent builds the right thing functionally, as the least solved category, and I think that’s exactly right. Structural and architectural harnesses have decades of existing tooling to lean on. Behavior doesn’t, which is why spec-driven development has become the default answer across essentially every serious coding agent tool in 2026: GitHub’s Spec Kit, AWS Kiro, OpenSpec, and Claude Code’s own spec workflows all converge on the same idea. Write an executable, version-controlled specification first, derive an implementation plan from it, break the plan into atomic tasks, and only then generate code.

For a .NET team, a minimal version of this costs almost nothing to adopt and pairs directly with the harness pieces above: a SPEC.md per feature, checked into the same PR as the code, that the behavior sensor (your test suite, plus an LLM-as-judge pass comparing the diff against the spec) verifies against before a human ever looks at the diff.

FEATURE-LEVEL SPEC TEMPLATE (drop this in specs/<feature>.md)

## Intent
One paragraph: what business problem this solves and why.

## Contract
- Inputs: exact types, validation rules
- Outputs: exact types, error cases
- Side effects: what gets persisted, what events fire

## Out of scope
Explicitly list what this task should NOT touch. This is the line
that keeps an agent from "helpfully" refactoring three unrelated files.

## Acceptance checks
- [] Test names or scenarios that must pass
- [] Architectural constraint that must hold (e.g. "no direct DbContext
      access outside the Infrastructure project")
Enter fullscreen mode Exit fullscreen mode

That “out of scope” section earns its place more than any other. The single most common failure I’ve hit with Claude Code on a real .NET solution isn’t wrong code, it’s correct code in a file nobody asked it to touch, because nothing told it where the task’s boundary actually was.

Don’t let the MCP layer break underneath you

If your harness includes custom MCP servers, and by 2026 most non-trivial .NET agent setups do, there’s a breaking change worth planning for now rather than discovering during an upgrade. MCP’s TypeScript SDK v2, built for the 2026–07–28 spec revision, restructures the package entirely: @modelcontextprotocol/sdk is gone, replaced by separate @modelcontextprotocol/server and @modelcontextprotocol/client packages, with the protocol itself moving to a stateless model and dropping sampling, roots, and logging as it existed in v1.

The good news, and the reason I’m not panicking about it: nothing forces this on you. A hand-constructed Client, Server, or McpServer from the old SDK keeps speaking the 2025-era protocol it was written against. Serving or speaking the new spec is an explicit opt-in, and there's a codemod for the mechanical parts of the migration:

npx @modelcontextprotocol/codemod@beta v1-to-v2 .
Enter fullscreen mode Exit fullscreen mode

If you’re building or consuming MCP servers as part of your .NET harness’s tool layer (most teams are, even from C#, since MCP servers are typically Node processes sitting behind a language-agnostic protocol boundary), budget time for this migration deliberately instead of discovering it when a dependency bump silently changes your tool schema mid-sprint.

Putting it together

None of these pieces are impressive alone. What changed my results wasn’t any single tool, it was treating the whole stack as one system to iterate on, the way Böckeler describes: watch what mistakes repeat, and every time one does, ask whether it needs a better guide (something computational if you can manage it) or a better sensor, not just a better sentence in a prompt.

A MINIMAL .NET HARNESS STACK FOR CLAUDE CODE

Layer What it does Example
--------------------------------------------------------------------------
Orchestration Loop, memory, approvals, Microsoft Agent
                         telemetry Framework Harness

Feedforward (guides) .NET-specific skills, dotnet-claude-kit
                         conventions, spec templates skills + SPEC.md

Feedback (sensors) Compiler-accurate structural Roslyn MCP tools,
                         checks, architecture tests ArchUnitNET

Tool layer Custom MCP servers exposing MCP TS SDK v2
                         internal APIs/data (opt-in migration)

Context isolation Subagents scoped to layers of memory: project
                         the solution, shared knowledge frontmatter +
                         piped explicitly shared file
Enter fullscreen mode Exit fullscreen mode

The teams getting genuinely reliable results from Claude Code on real .NET codebases in 2026 aren’t the ones with the best prompts. They’re the ones who stopped treating the agent’s context window as the only lever they have, and started building the boring, deterministic scaffolding around it instead.

Tags: .NET, Claude Code, AI Agents, Software Architecture, DevOps

Top comments (0)