Skip to content

Twelve layers between Claude and your production .NET code

The complete guardrail stack for running coding agents on production C# — twelve mechanisms, ordered as a maturity model, each with the config that makes it real.

Vukasin Vulovic 12 min read
Twelve layers between Claude and your production .NET code

Somewhere in your organization, a developer is already running a coding agent against your codebase. The question a CTO has to be able to answer is not whether — it is what stands between that agent and production? For most teams the honest answer is: a CLAUDE.md someone wrote in an afternoon, and code review. That is two layers, one of them advisory and the other one tired.

This post is the complete inventory of ours: twelve mechanisms, presented as a maturity model, each with the concrete artifact — a hook config, a formula, a marker, a table — that makes it real rather than aspirational. Six of the twelve are established practice you will find across the industry; six are our own contributions, built because nothing public existed when we needed them, and several now documented in dedicated deep-dives. By the end you will know which layers you already have, which ones your failure modes are begging for, and what the adoption path looks like.

One framing note before the list. The closest first-party analogue is GitHub Copilot’s modernize-dotnet workflow — assess → plan → execute — which structures the plan. The twelve layers cover that ground and then keep going, because in our experience the expensive agent failures happen during execution: the hallucinated API three steps into a refactor, the edit-thrash loop at hour two, the architectural drift nobody notices until review. Plans don’t fail codebases; unsupervised execution does.

The maturity model

Most teams sit at zero to two layers (a CLAUDE.md and CI). The layers below are numbered by their position in the system, not the order you adopt them — the adoption path comes after the inventory.

Layer 1 — Investigation-gate hook (our contribution)

A PreToolUse hook that blocks any Edit, Write, or Bash call referencing a symbol the agent has not actually read into context. Registered in .claude/settings.json:

{ "matcher": "Edit|Write|Bash",
  "hooks": [{ "type": "command", "command": "node scripts/investigation-gate.js" }] }

The gate converts “please investigate before implementing” from a CLAUDE.md request into a physical property of the loop: the edit that calls OrderService.Cancel cannot execute until the file that would reveal the method is actually CancelAsync(CancelCommand) has been opened. Hallucinated APIs — the single most common agent failure on .NET — die here, before the compiler and long before a reviewer. Full definition →

Layer 2 — Code-extracted pattern snapshots (established practice)

An agent-readable catalogue of the conventions actually present in your repo, generated from the code rather than written by hand:

endpoints: FastEndpoints classes, one per file, suffix "Endpoint"
dtos: records, suffix "Request"/"Response", no entities across the wire
async: suffix "Async" on public methods only

Every implementing subagent reads the snapshot before writing code, which is why the output lands in review reading like your team wrote it. Because snapshots are extracted, not authored, they cannot drift into aspiration — they document the codebase you have. Full definition →

Layer 3 — Bayesian build-outcome confidence (our contribution)

Every task run logs an outcome record — pattern used, build result, test result, retry count. A scorer aggregates them per pattern with pseudo-counts and time decay:

score = (successes + 2) / (evidence + 2) × (10.01)^weeks_since_validated

The number feeds the agent’s own scheduling: high-confidence steps proceed, middling ones get batched so one build verdict covers them, low ones pause for review before the tokens are spent. On our internal template build this layer flagged two architectural steps that would each have cost a sprint to unwind. The full mechanism — including the plan-time cascade-risk analyzer that complements it — is in the dedicated post.

Layer 4 — AUTO-TABLE inventories (our contribution)

Deterministic scanners regenerate structured inventories — endpoints, entities, consumers, migrations — directly inside the docs the agent reads, between paired markers:

<!-- AUTO-TABLE:endpoints:Features/Billing/Endpoints -->

Sixteen scanner types, regenerated per PR, which makes the table’s diff an architectural diff of the change — reviewable by humans, trustable by agents, and citable as audit evidence. Every fresh session opens against the same current snapshot of the codebase instead of re-deriving it slightly wrong. Deep-dive →

Layer 5 — Hybrid BM25 + embedding search (established practice)

One MCP search tool backed by both lexical and vector retrieval, merged with reciprocal-rank fusion. Identifier-grade queries (CancelPlanEndpoint) and concept-grade queries (“where do we throttle webhook retries?”) both land — the agent does not need to know which kind of question it is asking. The practical effect is on the duplication failure mode: the agent finds the existing implementation instead of writing a second one, because both search styles were looking. Full definition →

Layer 6 — Roslyn .NET MCP server (established practice)

The compiler’s semantic model exposed as agent tools — nine of them in ours, from find_symbol through get_dependency_graph:

find_references(IPaymentProvider) → 3 implementations, file:line each, compiler-verified

This is the layer that makes .NET the strongest stack for agentic work rather than an afterthought: the agent edits what a symbol is, not what it looks like in text. Overloads, interface implementations, and renames stop being guesswork. Deep-dive →

Layer 7 — Subagents with model tiering (established practice)

Cheap models for searches and inventory, mid-tier for implementation inside a verified plan, top-tier for planning and failure analysis:

export CLAUDE_CODE_SUBAGENT_MODEL=claude-sonnet-5   # per-agent overrides in .claude/agents/

Beyond the cost curve — per-PR spend becomes a function of plan size, not session length — tiering is a correctness tool: the expensive reasoning happens exactly where wrong answers are expensive, and nowhere else. Full definition →

Layer 8 — Runtime supervisors (established practice)

Hooks fire inside tool events; the supervisor runs beside the session, holding the state no single event can see — token spend against budget, oscillation-hash windows, drift between the live session and the plan it committed to. When a threshold trips, it stops the session and escalates to a human. This is the layer that makes unattended work — overnight migrations, CI-triggered fixes — defensible rather than reckless. Hooks report; the supervisor decides. Full definition →

Layer 9 — Oscillation detector hook (our contribution)

Every edit is fingerprinted as a transition hash; any proposed edit whose reverse appears in recent history is blocked before it lands:

OSCILLATION DETECTED in OrderTests.cs: you've changed this code back and forth
3 times. Stop and rethink: (1) root cause? (2) different approach? (3) ask the user?

The A→B→A loop — usually two test fixtures encoding contradictory assumptions — gets interrupted at the moment of decision, and the agent’s most common response is the correct one: surfacing the contradiction to a human. To our knowledge this pattern had no public implementation when we built it. Deep-dive →

Layer 10 — Code-existence inline annotations (our contribution)

Every symbol reference in a plan carries its evidence status:

3. Call OrderService.CancelAsync(cmd)   [VERIFIED — OrderService.cs:57]
4. Emit OrderCancelled to the outbox    [ASSUMED — publisher symbol unread]

Steps carrying [ASSUMED] or [MISSING] cannot execute — the investigation gate checks the annotations mechanically. This is what turns plan drift on long sessions from a confabulation risk into a checkable property: the 95%-correct plan with a confidently-wrong step four gets caught at step four, not in production. Full definition →

Layer 11 — Spec-driven CLAUDE.md (established practice)

The repository’s operating manual for agents, written as enforceable spec rather than prose — checkable rules, stated exceptions, a definition of done:

A task is complete when: dotnet build passes, dotnet test passes, new code has
tests matching the existing style. "It compiles" is not done.

Chronologically this is layer one — it costs an afternoon and improves everything downstream — and it is the natural first step for any team. Our full annotated template for .NET 10 + Aspire + EF Core 10 is public in the dedicated post.

Layer 12 — PostToolUse build gate (our contribution)

dotnet build plus scoped dotnet test after every multi-file edit, with a trimmed failure report — first error per file, failing test names — fed straight back into the agent’s context:

{ "matcher": "Edit|Write",
  "hooks": [{ "type": "command", "command": "node scripts/hooks/build-gate.js" }] }

Broken intermediate states never accumulate past a single step, which changes the shape of agent failures: instead of an end-of-session archaeology dig through ten compounded edits, you get one step-local fix while the agent still holds the context to make it. This layer also generates the evidence stream that layer 3 learns from. Full definition →

Which failure does which layer catch?

The CTO-grade view — map your incidents to the mechanism that would have prevented them:

Failure you have seenLayer that catches it
Agent calls an API that doesn’t exist1 (gate), 10 (annotations), 12 (build gate)
Generated code doesn’t look like your code2 (snapshots), 11 (CLAUDE.md)
Confident execution of a step that keeps failing3 (confidence)
Duplicate implementation of existing logic4 (inventories), 5 (hybrid search)
Wrong overload, missed implementation, bad rename6 (Roslyn MCP)
Token bill scales with session length, not output7 (tiering)
Overnight session burned budget going nowhere8 (supervisor), 9 (oscillation)
Edit-thrash: same lines flipped back and forth9 (oscillation)
Plan references files that don’t exist10 (annotations), 1 (gate)
Ten edits compile individually, break as a set12 (build gate)

No single layer is the moat — the composition is. Annotations feed the gate; the build gate feeds the confidence model; the oscillation detector reports to the supervisor; the inventories and snapshots give every session the context the rest assume. Remove one and its neighbors weaken.

The adoption path (not the numbered order)

Stage one — the afternoon (layers 11, 12, 1). Write the spec-driven CLAUDE.md, add the build gate, add the investigation gate. Roughly 20% of the system, and in our experience of instrumenting client repos, more than half its value. Every artifact needed is public in this cluster’s posts.

Stage two — the ground truth (layers 6, 4, 5, 2). Stand up the Roslyn MCP server, then the inventories, then hybrid search and snapshots as the solution grows past what an agent holds in context. This is where “the agent is fast” becomes “the agent is fast and right.”

Stage three — the statistics and the watchdogs (layers 3, 9, 10, 8, 7). Outcome logging, confidence scoring, annotations, supervisors, tiering. These matter most for long sessions, unattended runs, and regulated environments where evidence of control is a deliverable — the territory covered in our regulated-SaaS use case.

Teams asking “is this overkill?” are usually asking at stage zero, where it is. Teams at stage one asking whether stage two pays are usually already losing the sessions that answer the question.

What a guarded session actually looks like

The layers sound bureaucratic in a list; in a live session they are mostly invisible until they aren’t. A representative hour, on a real solution:

The session opens. The agent reads the CLAUDE.md (11), the current AUTO-TABLE inventories (4), and the pattern snapshots (2) — call it ninety seconds of orientation against ground truth instead of twenty minutes of exploratory grepping. The task is a new billing endpoint. The planning pass runs on the top model tier (7), and the plan comes back with every symbol reference annotated (10): eleven [VERIFIED], one [ASSUMED] — a webhook publisher the planner inferred but did not read. The investigation gate (1) refuses that step as written; the agent resolves it with two Roslyn MCP calls (6) — find_symbol, then get_implementations — upgrades the annotation, and execution starts on the mid tier (7).

Twenty minutes in, the build gate (12) catches a compile error two seconds after the edit that caused it; the agent fixes it in-context, and the outcome log quietly feeds the confidence scorer (3). At minute forty, the interesting moment: two test fixtures disagree about invoice rounding, the agent flips the same assertion twice, and the oscillation detector (9) blocks the third flip with a diagnostic. The agent does what the diagnostic suggests — stops and asks. A human answers one question, the contradiction dissolves, and the session ends with a green build, a PR whose inventory diff (4) shows exactly one new endpoint and one modified consumer, and a supervisor log (8) showing 61% of token budget used, no interventions required.

Nothing in that hour required a human watching. Two moments required a human deciding — and the system is what found those two moments and brought them to you, instead of burying them in forty minutes of confident wrongness.

The questions your security review will ask

Rolling this out at an organization with a real security function means answering four questions; here are ours.

What can the agent actually touch? Whatever the hooks allow. The gate scripts run on every tool call, they are versioned in your repo, and denial is the default posture for anything referencing unread symbols. Secrets are excluded by policy in the CLAUDE.md and by the hook layer — reference-by-name, never by value.

Whose code are the guardrails? Yours, after install. Every hook is a readable script in scripts/, every config is in .claude/, and nothing phones home to us. This is a deliberate design constraint: a guardrail system your team cannot audit is just a different vendor risk.

What is logged, and can we produce it? Every tool call (hook logs), every build verdict (outcome log), every architectural change (inventory diffs, per PR). For regulated environments this is the difference between “we reviewed the AI’s code” and evidence — the theme our regulated-SaaS use case covers in depth.

What happens when it fails? Layers fail open individually (a crashed hook allows the call rather than wedging the session) and fail safe collectively (the supervisor stops sessions that drift past thresholds). The failure story is a stopped session and a notification — never a silent bypass.

What this changes commercially

The reason a five-person boutique publishes its guardrail stack in public detail is that the stack is the product economics. The internal template build that anchors our End-to-End engagements — 23 features, 5 applications, six weeks — was the twelve layers’ proving run, and the reason a $20K floor is credible for work that conventionally quotes at $50K–$150K. The mechanism compresses the mechanical middle of development; the layers make the compression safe enough to sell with our name on it. The full evaluation-grade treatment — agent landscape, failure modes, economics, when not to do this — is in the field guide to AI-augmented .NET development.

If you want to see the twelve layers against your own codebase — which ones you have, which your incident history is asking for, what stage one would look like on your solution — book a one-hour walkthrough. It is $80, it is with the principal engineer who runs this system daily, and it ends with a written layer-by-layer assessment whether or not you ever engage us again. The two-week install of the full stack is Sprint Zero; the walkthrough is how you find out if it is worth it.