Generate Testability Wrappers
dotnet/skills
DO NOT USE when the target already consumes an injected interface or built-in abstraction such as IFileSystem or TimeProvider, even if the request says "generate a wrapper"; no new wrapper is needed.
Applies extend-only design rules to NuGet packages and distributed systems, covering source, binary and wire compatibility and how to deprecate members safely.
$ npx skills add Aaronontheweb/dotnet-skills --skill api-design -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install Aaronontheweb/dotnet-skills api-design --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/Aaronontheweb/dotnet-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/csharp-api-design .claude/skills/api-design && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "api-design" agent skill from https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/csharp-api-design into .claude/skills/api-design/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "api-design", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/csharp-api-designType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add Aaronontheweb/dotnet-skills --skill api-design -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install Aaronontheweb/dotnet-skills api-design --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Aaronontheweb/dotnet-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/csharp-api-design .agents/skills/api-design && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "api-design" agent skill from https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/csharp-api-design into .agents/skills/api-design/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "api-design", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Aaronontheweb/dotnet-skills --skill api-design -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install Aaronontheweb/dotnet-skills api-design --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Aaronontheweb/dotnet-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/csharp-api-design .cursor/skills/api-design && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "api-design" agent skill from https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/csharp-api-design into .cursor/skills/api-design/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "api-design", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/Aaronontheweb/dotnet-skills.git --path skills/csharp-api-design--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add Aaronontheweb/dotnet-skills --skill api-design -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install Aaronontheweb/dotnet-skills api-design --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Aaronontheweb/dotnet-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/csharp-api-design .gemini/skills/api-design && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "api-design" agent skill from https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/csharp-api-design into .gemini/skills/api-design/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "api-design", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install Aaronontheweb/dotnet-skills api-designInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add Aaronontheweb/dotnet-skills --skill api-design -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/Aaronontheweb/dotnet-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/csharp-api-design .github/skills/api-design && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "api-design" agent skill from https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/csharp-api-design into .github/skills/api-design/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "api-design", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Aaronontheweb/dotnet-skills --skill api-design -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install Aaronontheweb/dotnet-skills api-design --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Aaronontheweb/dotnet-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/csharp-api-design .opencode/skills/api-design && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "api-design" agent skill from https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/csharp-api-design into .opencode/skills/api-design/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "api-design", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
api-designApplies extend-only design rules to NuGet packages and distributed systems, covering source, binary and wire compatibility and how to deprecate members safely.
The skill distinguishes three kinds of compatibility a public API change can break: source or API compatibility, meaning code still compiles against the newer version; binary compatibility, meaning compiled code still runs against it without recompiling; and wire compatibility, meaning serialized data stays readable across versions on the network or in storage. Its core rule is extend-only design, resting on three pillars: previously released behavior and signatures are immutable, new functionality arrives through new constructs such as overloads or opt-in types rather than changes to existing ones, and removal happens only after a deprecation period measured in years, not releases.
Concrete C# examples separate safe changes, such as adding a new overload that delegates to an existing method, from unsafe ones, such as removing or renaming a public member, which are reserved for a major version if ever done at all. A deprecation pattern shows marking a member Obsolete with the version and replacement noted, which can happen in any release. To catch accidental breaking changes automatically, the skill sets up API approval testing with the PublicApiGenerator and Verify.Xunit NuGet packages, generating a verified text file of the public surface that a test compares against on every run.
3 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit e426ed9. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
dotnetFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
aaronstannard.comgetakka.netsemver.orggithub.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
.NET API Compatibility Design loads about 2.7k tokens when it runs. Until then it costs about 48 tokens; SKILL.md has 587 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from Aaronontheweb/dotnet-skills at commit e426ed9, republished under its MIT licence (© Aaronontheweb). 587 words, ~2,699 tokens.
.claude/skills/api-design/SKILL.md (or your agent's skills folder).Use this skill when:
| Type | Definition | Scope |
|---|---|---|
| API/Source | Code compiles against newer version | Public method signatures, types |
| Binary | Compiled code runs against newer version | Assembly layout, method tokens |
| Wire | Serialized data readable by other versions | Network protocols, persistence formats |
Breaking any of these creates upgrade friction for users.
The foundation of stable APIs: never remove or modify, only extend.
Resources:
// SAFE: Add NEW overload methods that delegate to existing methods
// Existing method - do not modify its signature
public void Process(Order order) { ... }
// New overload - safe to add
public void Process(Order order, CancellationToken ct)
{
// implementation that handles cancellation
}
// SAFE: Add NEW overloads for additional functionality
// Existing method - do not modify
public void Send(Message msg) { ... }
// New overload - safe to add
public void Send(Message msg, Priority priority)
{
// implementation that handles priority
}
// ADD new types, interfaces, enums
public interface IOrderValidator { }
public enum OrderStatus { Pending, Complete, Cancelled }
// ADD new members to existing types
public class Order
{
public DateTimeOffset? ShippedAt { get; init; } // NEW
}// REMOVE or RENAME public members
public void ProcessOrder(Order order); // Was: Process()
// CHANGE parameter types or order
public void Process(int orderId); // Was: Process(Order order)
// CHANGE return types
public Order? GetOrder(string id); // Was: public Order GetOrder()
// CHANGE access modifiers
internal class OrderProcessor { } // Was: public
// ADD optional parameters to EXISTING methods (binary incompatible!)
// The compiled IL method signature changes - callers compiled against
// the old signature will get MissingMethodException at runtime.
// Optional parameter defaults are baked into the CALLER's assembly at compile time.
public void Process(Order order, CancellationToken ct = default); // Breaks binary compat!
public void Send(Message msg, Priority priority = Priority.Normal); // Breaks binary compat!
// Correct approach: add a NEW overload method instead (see Safe Changes above)
// ADD required parameters without defaults
public void Process(Order order, ILogger logger); // Breaks callers!// Step 1: Mark as obsolete with version (any release)
[Obsolete("Obsolete since v1.5.0. Use ProcessAsync instead.")]
public void Process(Order order) { }
// Step 2: Add new recommended API (same release)
public Task ProcessAsync(Order order, CancellationToken ct = default);
// Step 3: Remove in next major version (v2.0+)
// Only after users have had time to migratePrevent accidental breaking changes with automated API surface testing.
dotnet add package PublicApiGenerator
dotnet add package Verify.Xunit[Fact]
public Task ApprovePublicApi()
{
var api = typeof(MyLibrary.PublicClass).Assembly.GeneratePublicApi();
return Verify(api);
}Creates ApprovePublicApi.verified.txt:
namespace MyLibrary
{
public class OrderProcessor
{
public OrderProcessor() { }
public void Process(Order order) { }
public Task ProcessAsync(Order order, CancellationToken ct = default) { }
}
}Any API change fails the test - reviewer must explicitly approve changes.
*.verified.txt filesFor distributed systems, serialized data must be readable across versions.
| Direction | Requirement |
|---|---|
| Backward | Old writers → New readers (current version reads old data) |
| Forward | New writers → Old readers (old version reads new data) |
Both are required for zero-downtime rolling upgrades.
Phase 1: Add read-side support (opt-in)
// New message type - readers deployed first
public sealed record HeartbeatV2(
Address From,
long SequenceNr,
long CreationTimeMs); // NEW field
// Deserializer handles both old and new
public object Deserialize(byte[] data, string manifest) => manifest switch
{
"Heartbeat" => DeserializeHeartbeatV1(data), // Old format
"HeartbeatV2" => DeserializeHeartbeatV2(data), // New format
_ => throw new NotSupportedException()
};Phase 2: Enable write-side (opt-out, next minor version)
// Config to enable new format (off by default initially)
akka.cluster.use-heartbeat-v2 = onPhase 3: Make default (future version)
After install base has absorbed read-side code.
Prefer schema-based formats over reflection-based:
| Format | Type | Wire Compatibility |
|---|---|---|
| Protocol Buffers | Schema-based | Excellent - explicit field numbers |
| MessagePack | Schema-based | Good - with contracts |
| System.Text.Json | Schema-based (with source gen) | Good - explicit properties |
| Newtonsoft.Json | Reflection-based | Poor - type names in payload |
| BinaryFormatter | Reflection-based | Terrible - never use |
See dotnet/serialization skill for details.
Mark non-public APIs explicitly:
// Attribute for documentation
[InternalApi]
public class ActorSystemImpl { }
// Namespace convention
namespace MyLibrary.Internal
{
public class InternalHelper { } // Public for extensibility, not for users
}Document clearly:
Types in
.Internalnamespaces or marked with[InternalApi]may change between any releases without notice.
// DO: Seal classes not designed for inheritance
public sealed class OrderProcessor { }
// DON'T: Leave unsealed by accident
public class OrderProcessor { } // Users might inherit, blocking changes// DO: Small, focused interfaces
public interface IOrderReader
{
Order? GetById(OrderId id);
}
public interface IOrderWriter
{
Task SaveAsync(Order order);
}
// DON'T: Monolithic interfaces (can't add methods without breaking)
public interface IOrderRepository
{
Order? GetById(OrderId id);
Task SaveAsync(Order order);
// Adding new methods breaks all implementations!
}| Version | Changes Allowed |
|---|---|
| Patch (1.0.x) | Bug fixes, security patches |
| Minor (1.x.0) | New features, deprecations, obsolete removal |
| Major (x.0.0) | Breaking changes, old API removal |
[Obsolete] for at least one minor versionBefore removing or changing something, understand why it exists.
Assume every public API is used by someone. If you want to change it:
When reviewing PRs that touch public APIs:
[Obsolete] instead).verified.txt changes reviewed)// "Bug fix" that breaks users
public async Task<Order> GetOrderAsync(OrderId id) // Was sync!
{
// "Fixed" to be async - but breaks all callers
}
// Correct: Add new method, deprecate old
[Obsolete("Use GetOrderAsync instead")]
public Order GetOrder(OrderId id) => GetOrderAsync(id).Result;
public async Task<Order> GetOrderAsync(OrderId id) { }// Changing defaults breaks users who relied on old behavior
public void Configure(bool enableCaching = true) // Was: false!
// Correct: New parameter with new name
public void Configure(
bool enableCaching = false, // Original default preserved
bool enableNewCaching = true) // New behavior opt-in// AVOID: Type names in wire format
{ "$type": "MyApp.Order, MyApp", "Id": 123 }
// Renaming Order class = wire break!
// PREFER: Explicit discriminators
{ "type": "order", "id": 123 }© Aaronontheweb, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/csharp-api-design of Aaronontheweb/dotnet-skills.
Open the folder on GitHubat commit e426ed9
We found 3 copies of this SKILL.md (exact, near-identical or edited) in other folders, from 2 other GitHub owners. This page covers the copy in Aaronontheweb/dotnet-skills, which our catalogue first saw on October 7, 2026.
.NET API Compatibility Design next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| .NET API Compatibility Design this skillAaronontheweb/dotnet-skills | 1.2k | 2 repos | ~2.7k | Automated safety check: Pass | MIT | |
| Generate Testability Wrappersdotnet/skills | 5.6k | 1 repos | ~3.9k | Automated safety check: Pass | MIT | |
| Generate Testability Wrappersmicrosoft/testfx | 1k | — | ~2.2k | Automated safety check: Pass | MIT | |
| Csharp Dotnetericrisco/rsc-harness | 156 | — | ~3.3k | Automated safety check: Pass | MIT | |
| Tsp Csharpquerylenshq/ef-querylens | 225 | — | ~1.3k | Automated safety check: Pass | MIT | |
| DisCatSharp Discord DevelopmentAiko-IT-Systems/DisCatSharp | 140 | — | ~1.2k | Automated safety check: Pass | MIT |
dotnet/skills
DO NOT USE when the target already consumes an injected interface or built-in abstraction such as IFileSystem or TimeProvider, even if the request says "generate a wrapper"; no new wrapper is needed.
microsoft/testfx
Generate wrapper interfaces and DI registration for hard-to-test static dependencies in C.
ericrisco/rsc-harness
A skill your agent uses when writing, reviewing, testing, or shipping C / .NET code — ASP.NET Core APIs (minimal APIs vs controllers), EF Core data access, async correctness, solution layout in…
querylenshq/ef-querylens
Comprehensive C and .NET development skill for TSP projects.
Aiko-IT-Systems/DisCatSharp
Helps build, explain and troubleshoot C# Discord apps on DisCatSharp, answering from the project's installed version and the library's documentation rather than guesses.
wshobson/agents
Master C/.NET backend development patterns for building robust APIs, MCP servers, and enterprise applications.
Aaronontheweb/dotnet-skills
Guides making .NET libraries trimming-safe and Native-AOT compatible: the MSBuild properties, trimming attributes, warning codes and a playbook of safe patterns.
Aaronontheweb/dotnet-skills
Shows how to build entity actors with Akka.Hosting so the same code runs in local unit tests and in a sharded cluster in production.
Aaronontheweb/dotnet-skills
Guidance for Akka.NET actor systems covering EventStream versus DistributedPubSub, supervision, Props versus DependencyResolver, work distribution and testable cluster code.
Aaronontheweb/dotnet-skills
Sets up Akka.Management and Cluster.Bootstrap so Akka.NET clusters form through service discovery on Kubernetes, Azure or config instead of static seed nodes.
Aaronontheweb/dotnet-skills
Shows how to test Akka.NET actors with Akka.Hosting.TestKit: swapping services for fakes, using TestProbes, and checking persistence, plus when the older TestKit still fits.
Aaronontheweb/dotnet-skills
Wires .NET Aspire AppHost resources into explicit environment-variable configuration, keeping application code free of Aspire client packages and service discovery.
Categories
Applies extend-only design rules to NuGet packages and distributed systems, covering source, binary and wire compatibility and how to deprecate members safely. The skill distinguishes three kinds of compatibility a public API change can break: source or API compatibility, meaning code still compiles against the newer version; binary compatibility, meaning compiled code still runs against it without recompiling; and wire compatibility, meaning serialized data stays readable across versions on the network or in storage. Its core rule is extend-only design, resting on three pillars: previously released behavior and signatures are immutable, new functionality arrives through new constructs such as overloads or opt-in types rather than changes to existing ones, and removal happens only after a deprecation period measured in years, not releases.
.NET API Compatibility Design fits situations like: designing a new public API for a NuGet package that must stay compatible across versions; deciding whether a proposed code change is safe or a breaking change; setting up automated API approval tests to catch breaking changes; deprecating a public member without breaking existing consumers.
Run `npx skills add Aaronontheweb/dotnet-skills --skill api-design -a claude-code`. Or copy the skill folder (skills/csharp-api-design in Aaronontheweb/dotnet-skills) into .claude/skills/api-design in your project. Claude Code loads it when a task matches its description.
Run `npx skills add Aaronontheweb/dotnet-skills --skill api-design -a codex`. Or copy the skill folder (skills/csharp-api-design in Aaronontheweb/dotnet-skills) into .agents/skills/api-design in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add Aaronontheweb/dotnet-skills --skill api-design -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/api-design, .gemini/skills/api-design, .github/skills/api-design and .opencode/skills/api-design in your project.
Going by SKILL.md and its folder, .NET API Compatibility Design needs the command-line tools its instructions call (dotnet). Our summary lists: .NET with the PublicApiGenerator and Verify.Xunit NuGet packages for approval testing.
SKILL.md names 4 domains. As links in the text: aaronstannard.com, getakka.net, semver.org and github.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
.NET API Compatibility Design is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 2.7k tokens (SKILL.md is roughly 11k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with .NET API Compatibility Design: Generate Testability Wrappers (dotnet/skills, 5.6k stars), Generate Testability Wrappers (microsoft/testfx, 1k stars), Csharp Dotnet (ericrisco/rsc-harness, 156 stars) and Tsp Csharp (querylenshq/ef-querylens, 225 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
Aaronontheweb (a GitHub user) maintains it in Aaronontheweb/dotnet-skills, which has 1,202 GitHub stars. The repository holds 33 skills in this directory. The repository was last updated on September 17, 2026.
Source: Aaronontheweb/dotnet-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.