MCPInstall

aot-trimming

Guidelines for making .NET libraries and applications trimming-safe and Native AOT compatible. Covers the trimming/AOT model, the MSBuild properties that enable analysis (IsTrimmable, IsAotCompatible, PublishTrimmed, PublishAot), the trimming attributes (RequiresUnreferencedCode, RequiresDynamicCode, DynamicallyAccessedMembers, UnconditionalSuppressMessage), IL2xxx/IL3xxx warning codes, and a pattern playbook: source generators and UnsafeAccessor, generated-interception diagnostic suppressors, intentional runtime scanning boundaries, trimming-safe islands and feature switches, migration analyzers, and temporary warning-approval baselines. Use when introducing trimming/AOT support, resolving IL2xxx/IL3xxx warnings, or making reflection-heavy code trimming-safe in C / .NET codebases. For System.Text.Json AOT scenarios, see also the serialization skill.

Installs
49
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/aaronontheweb/dotnet-skills --skill aot-trimming
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads aot-trimming automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

.NET Trimming and Native AOT

When to Use

  • Adding trimming (PublishTrimmed) or Native AOT (PublishAot) support to a library or application
  • Resolving IL2xxx (trimming) and IL3xxx (AOT/single-file) warnings
  • Making reflection-heavy code trimming-safe
  • Replacing runtime reflection, assembly scanning, or Reflection.Emit with compile-time alternatives
  • Reviewing a codebase or PR for trimming/AOT compatibility, including generated interceptors and analyzer suppressors
  • Designing public APIs that must carry trimming annotations

Core Goals

  • Produce code that survives the linker's reachability analysis and runs under Native AOT (no JIT).
  • Express what the compiler cannot see — which members reflection needs — using the trimming attributes.
  • Keep trimming-safe and trimming-unsafe code separated so unsafe paths are explicit and contained.

Core Model

Trimming vs Native AOT

  • Trimming (PublishTrimmed) runs ILLink reachability analysis: only statically reachable code is kept. Reflection the analyzer cannot see is trimmed away and produces warnings.
  • Native AOT (PublishAot) makes trimming mandatory and removes the JIT. Reflection.Emit is unsupported, constructing unknown generic instantiations at runtime is not guaranteed, Expression.Compile() may fall back to interpretation, and reflection works only over members the linker preserved.
  • Both run the same static analysis:
    • IL2xxx — trimming warnings: RequiresUnreferencedCode propagation + DynamicallyAccessedMembers dataflow.
    • IL3xxx — AOT and single-file warnings: RequiresDynamicCode / RequiresAssemblyFiles.

A library can be trimmable but not AOT-compatible (it uses Reflection.Emit, which trimming tolerates but AOT does not).

The two problems to solve

  1. Unseen reflection (trimming): the linker removes a member or type you load by name, or reach through typeof(T) in a generic method.
  2. Dynamic code generation (AOT): the JIT is gone, so Reflection.Emit is unsupported and constructing unknown generic instantiations is not guaranteed.

Both are solved the same way: make the required members statically visible to the analyzer, or remove the need for reflection entirely.

Reflection is not the enemy

Reflection is a legitimate technique and is completely fine in ordinary JIT applications — you need none of this there. The trimming attributes exist to allow reflection under trimming/AOT, not to forbid it: annotate what reflection needs, and the linker keeps it. Reach for source generators, [UnsafeAccessor], or capability gating only when you want a specific reflective surface removed or contained (for example, a public registration API you do not want to ship with [RequiresUnreferencedCode]).

Project Configuration

For a library, declare compatibility so the analyzers surface warnings and consumers know:

<PropertyGroup>
  <!-- Trim-compatible: enables trim warnings. -->
  <IsTrimmable>true</IsTrimmable>

  <!-- AOT-compatible: implies IsTrimmable + EnableTrimAnalyzer + EnableSingleFileAnalyzer + EnableAotAnalyzer. -->
  <IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">true</IsAotCompatible>
</PropertyGroup>

For an application, publish trimmed or AOT:

<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
  <!-- or -->
  <PublishAot>true</PublishAot>
</PropertyGroup>

Verify by actually publishing — a trimmed/AOT app must produce zero warnings:

dotnet publish -c Release -r <rid> -p:PublishTrimmed=true
dotnet publish -c Release -r <rid> -p:PublishAot=true

A test app that roots the library is the standard way to validate a library:

<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
<ItemGroup>
  <ProjectReference Include="..\MyLibrary\MyLibrary.csproj" />
  <TrimmerRootAssembly Include="MyLibrary" />
</ItemGroup>

See aot-trimming-playbook-reference.md for the full property table, feature switches, and the warning-approval baseline pattern.

The Attribute Model

These live in System.Diagnostics.CodeAnalysis. See trimming-attributes-reference.md for the full catalog with exact signatures, warning codes, and rules.

  • [RequiresUnreferencedCode(message)] — marks code that needs members the linker cannot see. Suppresses warnings inside; emits IL2026 at every call site.
  • [RequiresDynamicCode(message)] — marks code that needs a JIT (emitting, dynamic). Emits IL3050 at call sites.
  • [DynamicallyAccessedMembers(memberTypes)] — declares which members of a Type/string must be preserved. Flows backward from the reflection site to the Type source. Scope it (and the other attributes) to a single accessor by placing the attribute directly on the accessor, or with the [method:], [field:], or [return:] targets.
  • [UnconditionalSuppressMessage(category, checkId, Justification = "...")] — last-resort, IL-persisted suppression; must carry a justification and a real invariant.
  • [DynamicDependency("Member", typeof(T))] — keeps named members but does not silence warnings.

Choose the narrowest DynamicallyAccessedMemberTypes for the reflection you actually perform:

ReflectionMember type
Activator.CreateInstance<T>(), new T(), Activator.CreateInstance(type)PublicParameterlessConstructor
DI activation (ActivatorUtilities.CreateFactory<T>)PublicConstructors
Explicit non-public activationNonPublicConstructors
type.GetInterfaces()Interfaces
type.GetMethods()PublicMethods (add NonPublicMethods when needed)
type.GetProperties()PublicProperties
type.GetFields()PublicFields
type.GetNestedTypes()PublicNestedTypes

The Pattern Playbook (selection)

First decide whether the code must be trimming/AOT safe at all. A plain JIT app or library needs none of this — reflection is fine there. Only when you or a consumer publish trimmed/AOT does reflection need to become visible to the linker.

The goal is never "remove all reflection"; it is make the reflection statically visible so the linker keeps what it needs. Choose per call site:

  • Keep the reflection and annotate it — the common case. Put [DynamicallyAccessedMembers(...)] on the Type/string source and propagate it through the chain; mark entry points [RequiresUnreferencedCode]/[RequiresDynamicCode]. The attributes exist precisely to let reflection work under trimming. See trimming-attributes-reference.md.

  • Replace the reflection with a compile-time alternative — when you want a reflective surface gone (for example, a public registration API you do not want to ship with [RequiresUnreferencedCode]). A source generator emits direct type references (marker attribute → generated registration); [UnsafeAccessor] replaces FieldInfo/MethodInfo/ConstructorInfo. See aot-trimming-playbook-reference.md and aot-trimming-playbook-reference.md.

  • Isolate a whole dynamic path — when a path is inherently dynamic (emitting proxies, loading plugins by name), move it behind a boundary and provide a trimming-safe alternative selected by RuntimeFeature.IsDynamicCodeSupported. See aot-trimming-playbook-reference.md.

  • Suppress only as a last resort — a single [UnconditionalSuppressMessage(...)] at an invariant-proven leaf, with a Justification. Never #pragma (not persisted in IL).

See aot-trimming-playbook-reference.md for the full playbook, including object-overload → generic-overload redirects, generated-interception suppressors, intentional scanning boundaries, migration analyzers, strict-mode precedence, and temporary warning-approval baselines.

Reference Files

  • trimming-attributes-reference.md: The complete attribute catalog — RequiresUnreferencedCode, RequiresDynamicCode, DynamicallyAccessedMembers (full DynamicallyAccessedMemberTypes list), UnconditionalSuppressMessage, DynamicDependency — each with intent, exact signature, and rules, plus the IL2xxx/IL3xxx warning-code table.
  • aot-trimming-playbook-reference.md: The full pattern playbook — source generators vs reflection, trimming-safe islands and capability guards, separating safe/unsafe code, System.Text.Json source generation, object-overload → generic-overload redirects with [OverloadResolutionPriority] and opt-in analyzers, migration analyzers, warning-approval baselines, known gotchas, and the full generation checklist.

Generation Checklist (Summary)

  1. Project — IsTrimmable/IsAotCompatible on libraries; PublishTrimmed/PublishAot on apps; zero warnings on publish.
  2. Make reflection visible (or replace it) — annotate the reflection you keep (DynamicallyAccessedMembers/RequiresUnreferencedCode/RequiresDynamicCode); replace it with source generators, generic (typeof(T)) APIs, or [UnsafeAccessor] only where you want the reflective surface gone.
  3. Annotate — narrowest DynamicallyAccessedMembers on every Type/string source; propagate through the chain; [RequiresUnreferencedCode]/[RequiresDynamicCode] on unsafe entry points.
  4. Isolate — keep unsafe reflective/emit code behind a boundary; gate dynamic-code paths with RuntimeFeature.IsDynamicCodeSupported, and separate trimming paths with #if or annotations.
  5. Suppress — only with [UnconditionalSuppressMessage] + justification at invariant-proven leaves; never #pragma/SuppressMessage.
  6. Verify — publish trimmed and AOT, run analyzers, and lock the warning surface with an approval baseline.

References

FAQ

What does the aot-trimming skill do?

Guidelines for making .NET libraries and applications trimming-safe and Native AOT compatible. Covers the trimming/AOT model, the MSBuild properties that enable analysis (IsTrimmable, IsAotCompatible, PublishTrimmed, PublishAot), the trimming attributes (RequiresUnreferencedCode, RequiresDynamicCode, DynamicallyAccessedMembers, UnconditionalSuppressMessage), IL2xxx/IL3xxx warning codes, and a pattern playbook: source generators and UnsafeAccessor, generated-interception diagnostic suppressors, intentional runtime scanning boundaries, trimming-safe islands and feature switches, migration analyzers, and temporary warning-approval baselines. Use when introducing trimming/AOT support, resolving IL2xxx/IL3xxx warnings, or making reflection-heavy code trimming-safe in C / .NET codebases. For System.Text.Json AOT scenarios, see also the serialization skill.

How do I install aot-trimming?

Run npx skills add https://github.com/aaronontheweb/dotnet-skills --skill aot-trimming in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/aot-trimming/ (personal) or .claude/skills/aot-trimming/ (project).

Which AI agents can use aot-trimming?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is aot-trimming safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains aot-trimming?

It is published by aaronontheweb in aaronontheweb/dotnet-skills. It has 49 recorded installs.

More from aaronontheweb/dotnet-skills

View all →

modern-csharp-coding-standards

aaronontheweb/dotnet-skills

Write modern, high-performance C code using records, pattern matching, value objects, async/await, Span /Memory , and best-practice API design patterns. Emphasizes functional-style programming with C 12+ features.

Backend & APIs2.2K installs

csharp-concurrency-patterns

aaronontheweb/dotnet-skills

Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.

Backend & APIs1.6K installs

efcore-patterns

aaronontheweb/dotnet-skills

Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.

dependency-injection-patterns

aaronontheweb/dotnet-skills

Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add methods for clean Program.cs and reusable configuration in tests.

Security676 installs

database-performance

aaronontheweb/dotnet-skills

Database access patterns for performance. Separate read/write models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.

Data & Databases672 installs

api-design

aaronontheweb/dotnet-skills

Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.

Backend & APIs667 installs

Popular backend & apis skills

View category →

find-skills

vercel-labs/skills

Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.

OfficialBackend & APIs4.1M installs

entra-app-registration

microsoft/azure-skills

Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.

OfficialBackend & APIs566.7K installs

This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).

Backend & APIs514.8K installs

embedded-captions

heygen-com/hyperframes

Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.

Backend & APIs453.3K installs

entra-agent-id

microsoft/azure-skills

Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).

OfficialBackend & APIs336.6K installs

neon

neondatabase/agent-skills

Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.

Backend & APIs237.1K installs