Best for
- "Make this project AOT-compatible"
- "Fix trimming warnings" or "fix IL warnings"
- "Resolve IL2070 / IL2067 / IL2072 / IL2026 / IL3050 warnings"
dotnet/skills/plugins/dotnet-upgrade/skills/dotnet-aot-compat/SKILL.md
Make .NET projects compatible with Native AOT and trimming by systematically resolving IL trim/AOT analyzer warnings. USE FOR: making projects AOT-compatible, fixing trimming warnings, resolving IL warnings (IL2026, IL2070, IL2067, IL2072, IL3050), adding DynamicallyAccessedMembers annotations, enabling IsAotCompatible. DO NOT USE FOR: publishing native AOT binaries, optimizing binary size, replacing reflection-heavy libraries with alternatives. INVOKES: no tools — pure knowledge skill.
Decision brief
Make .NET projects compatible with Native AOT and trimming by systematically resolving all IL trim/AOT analyzer warnings.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/dotnet/skills --skill "plugins/dotnet-upgrade/skills/dotnet-aot-compat"Inspect the Agent Skill "dotnet-aot-compat" from https://github.com/dotnet/skills/blob/1b896e91feb0f613cb54a914f1efd2897810ae02/plugins/dotnet-upgrade/skills/dotnet-aot-compat/SKILL.md at commit 1b896e91feb0f613cb54a914f1efd2897810ae02. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Do not explore the codebase up-front. The build warnings tell you exactly which files and lines need changes. Follow a tight loop: build → pick a warning → open that file at that line → apply the fix recipe → rebuild. Reading or analyzing source files beyond what a specific warn…
Add IsAotCompatible. If the project doesn't exclusively target net8.0+, add a TFM condition (AOT analysis requires net8.0+):
Sort and deduplicate. Common warning codes: - IL2070: Reflection call on a Type parameter missing [DynamicallyAccessedMembers] - IL2067: Passing an unannotated Type to a method expecting [DynamicallyAccessedMembers] - IL2072: Return value or extracted value missing annotation (o…
Group the warnings from Step 2 by warning code and count them. Do not open individual files yet. Identify the top 1-2 patterns by count — these drive your fix strategy:
Work from the innermost reflection call outward. Each fix may cascade new warnings to callers.
Permission review
The documentation asks the agent to read local files, directories, or repositories.
**Do not explore the codebase up-front.** The build warnings tell you exactly which files and lines need changes. Follow a tight loop: **build → pick a warning → open that file at that line → apply the fix recipe → rebuild**. Reading or anaThe documentation asks the agent to read local files, directories, or repositories.
### Step 3: Triage warnings by code (do NOT read every file)Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 98/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 5,248 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Make .NET projects compatible with Native AOT and trimming by systematically resolving all IL trim/AOT analyzer warnings.
Do not use this skill when the project exclusively targets .NET Framework (net4x), which does not support the trim/AOT analyzers.
An existing .NET project targeting net8.0 or later (or multi-targeting with at least one net8.0+ TFM) and the corresponding .NET SDK installed.
Native AOT and the IL trimmer perform static analysis to determine what code is reachable. Reflection can break this analysis because the trimmer can't see what types/members are accessed at runtime. The IsAotCompatible property enables analyzers that flag these issues as build warnings (ILXXXX codes).
#pragma warning disable for IL warnings. It hides warnings from the Roslyn analyzer at build time, but the IL linker and AOT compiler still see the issue. The code will fail at trim/publish time.[UnconditionalSuppressMessage]. It tells both the analyzer AND the linker to ignore the warning, meaning the trimmer cannot verify safety. Raising an error at build time is always preferable to hiding the issue and having it silently break at runtime.[DynamicallyAccessedMembers] annotations to flow type information through the call chain.Type through object[]).[RequiresUnreferencedCode] / [RequiresDynamicCode] / [RequiresAssemblyFiles] to mark methods as fundamentally incompatible with trimming, propagating the requirement to callers. This surfaces the issue clearly rather than hiding it — callers must explicitly acknowledge the incompatibility.The trimmer tracks [DynamicallyAccessedMembers] annotations through assignments, parameter passing, and return values. If this flow is broken (e.g., by boxing a Type into object, storing in an untyped collection, or casting through interfaces), the trimmer loses track and warns. The fix is to preserve the flow, not suppress the warning.
Do not explore the codebase up-front. The build warnings tell you exactly which files and lines need changes. Follow a tight loop: build → pick a warning → open that file at that line → apply the fix recipe → rebuild. Reading or analyzing source files beyond what a specific warning points you to is wasted effort and leads to timeouts. Let the compiler guide you.
❌ Do NOT run
find,ls, orgrepto understand the project structure before building. Do NOT read README, docs, or architecture files. Your first action should be Step 1 (enable AOT analysis), then build.
Add IsAotCompatible. If the project doesn't exclusively target net8.0+, add a TFM condition (AOT analysis requires net8.0+):
<PropertyGroup>
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">true</IsAotCompatible>
</PropertyGroup>
This automatically sets EnableTrimAnalyzer=true and EnableAotAnalyzer=true for compatible TFMs. For multi-targeting projects (e.g., netstandard2.0;net8.0), the condition ensures no NETSDK1210 warnings on older TFMs.
dotnet build <project.csproj> -f <net8.0-or-later-tfm> --no-incremental 2>&1 | grep 'IL[0-9]\{4\}'
Sort and deduplicate. Common warning codes:
Type parameter missing [DynamicallyAccessedMembers]Type to a method expecting [DynamicallyAccessedMembers]Type.GetType(string) with a non-constant argument[RequiresUnreferencedCode][DynamicallyAccessedMembers] required by constraintAssembly.Location returns empty string in single-file/AOT apps[RequiresDynamicCode]Group the warnings from Step 2 by warning code and count them. Do not open individual files yet. Identify the top 1-2 patterns by count — these drive your fix strategy:
| Pattern | Typical fix |
|---|---|
Many IL2026 + IL3050 from JsonSerializer | Go to Strategy C immediately — create a JsonSerializerContext, then batch-update all call sites |
IL2070/IL2087 on Type parameters | Add [DynamicallyAccessedMembers] to the innermost method, then cascade outward |
IL2067 passing unannotated Type | Annotate the parameter at the source |
In most real projects, IL2026/IL3050 from JsonSerializer dominate. Start with Strategy C unless the warning breakdown clearly shows otherwise. After the batch JSON fix, handle remaining warnings with Strategies A–B. Only use Strategy D as a last resort.
Work from the innermost reflection call outward. Each fix may cascade new warnings to callers.
Stay warning-driven. For each warning, open only the file and line the compiler reported, identify the pattern, apply the matching fix recipe below, and move on. Do not scan the codebase for similar patterns or try to understand the full architecture — fix what the compiler tells you, rebuild, and let new warnings guide the next change. Fix a small batch of warnings (5-10), then rebuild immediately to check progress.
Use sub-agents when available. If you can launch sub-agents (e.g., via a task tool), dispatch multiple sub-agents in parallel to edit different files simultaneously. Keep the main loop focused on building, parsing warnings, and dispatching — delegate actual file edits to sub-agents. For batch JSON updates, give each sub-agent 5-10 files to update in one prompt. After 2 build-fix cycles, dispatch all remaining file edits to sub-agents in parallel — do not continue fixing files sequentially. Example:
Update these files to use source-generated JSON:
src/Models/Resource.Serialization.cs,src/Models/Identity.Serialization.cs,src/Models/Plan.Serialization.cs. In each file, replaceJsonSerializer.Serialize(writer, value)withJsonSerializer.Serialize(writer, value, MyProjectJsonContext.Default.TypeName)andJsonSerializer.Deserialize<T>(ref reader)withJsonSerializer.Deserialize(ref reader, MyProjectJsonContext.Default.TypeName). Only edit the JsonSerializer call sites.
[DynamicallyAccessedMembers] (preferred)When a method uses reflection on a Type parameter, annotate the parameter to tell the trimmer what members are needed:
using System.Diagnostics.CodeAnalysis;
// Before (warns IL2070):
void Process(Type t) {
var method = t.GetMethod("Foo"); // trimmer can't verify
}
// After (clean):
void Process([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type t) {
var method = t.GetMethod("Foo"); // trimmer preserves public methods
}
When you annotate a parameter, all callers must now pass properly annotated types. This cascades outward — follow each caller and annotate or refactor as needed. The caller's annotation must include at least the same member types as the callee's. If the callee requires PublicConstructors | NonPublicConstructors, the caller must specify the same or a superset — using only NonPublicConstructors will produce IL2091.
When annotation flow is broken by boxing (storing Type in object, object[], or untyped collections), refactor to pass the Type directly:
// BROKEN: Type boxed into object[], annotation lost
void Process(object[] args) {
Type t = (Type)args[0]; // IL2072: annotation lost through boxing
Evaluate(t, ...);
}
// FIXED: Pass Type as a separate, annotated parameter
void Process(
object[] args,
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type calleeType,
...) {
Evaluate(calleeType, ...); // annotation flows cleanly
}
Common patterns that break flow and how to fix them:
object[] parameter bags: Extract the Type into a dedicated annotated parameterWhen most warnings are IL2026/IL3050 from JsonSerializer.Serialize/Deserialize, this is a single mechanical fix applied in bulk:
Collect affected types — grep for all JsonSerializer.Serialize and JsonSerializer.Deserialize call sites. Extract the type being serialized (the <T> in Deserialize<T>, or the runtime type of the object in Serialize).
Create one JsonSerializerContext with [JsonSerializable] for every type found. Skip types from external packages (e.g., ResponseError from Azure.Core) — they won't source-generate for types you don't own. Handle external types separately via Gotcha #1 below.
[JsonSerializerContext]
[JsonSerializable(typeof(ManagedServiceIdentity))]
[JsonSerializable(typeof(SystemData))]
// ... one attribute per type YOU OWN
// Do NOT add types from external packages (e.g., ResponseError)
internal partial class MyProjectJsonContext : JsonSerializerContext { }
Batch-update all call sites — do not read each file individually. Apply the pattern mechanically:
JsonSerializer.Serialize(obj) → JsonSerializer.Serialize(obj, MyProjectJsonContext.Default.TypeName)JsonSerializer.Deserialize<T>(json) → JsonSerializer.Deserialize(json, MyProjectJsonContext.Default.TypeName)Find and update all call sites in one pass:
# Find all files with JsonSerializer calls
grep -rl 'JsonSerializer\.\(Serialize\|Deserialize\)' src/ --include='*.cs'
Then use sequential edit calls to apply the same transformation to every matching file. Do not use sed for C# code — generics like Deserialize<T>() have angle brackets and nested parentheses that sed will mangle.
Build once to verify. Remaining warnings will be non-serialization issues — handle those with Strategies A–B or D.
[RequiresUnreferencedCode] (last resort)When a method fundamentally requires arbitrary reflection that cannot be statically described:
[RequiresUnreferencedCode("Loads plugins by name using Assembly.Load")]
public void LoadPlugin(string assemblyName) {
var asm = Assembly.Load(assemblyName);
// ...
}
This propagates to callers — they must also be annotated with [RequiresUnreferencedCode]. Use sparingly; it marks the entire call chain as trim-incompatible.
After each small batch of fixes (5-10 warnings), rebuild with --no-incremental and check for new warnings. Do not attempt to fix all warnings before rebuilding — frequent rebuilds catch mistakes early and reveal cascading warnings. Fixes cascade — annotating an inner method may surface warnings in its callers. Repeat until 0 Warning(s).
Build all target frameworks to ensure:
IsAotCompatible condition handles this)dotnet build <project.csproj> # builds all TFMs
[RequiresUnreferencedCode].For multi-targeting projects that include netstandard2.0 or net472, you need polyfills for DynamicallyAccessedMembersAttribute and related types. See references/polyfills.md.
ResponseError from Azure.Core) and it lacks a source-generated serializer, Options.GetConverter<T>() is reflection-based and will produce IL warnings. First check if the type implements IJsonModel<T> (common in Azure SDK) — if so, bypass JsonSerializer entirely:// Before (IL2026 — JsonSerializer uses reflection):
JsonSerializer.Serialize(writer, errorValue);
// After (AOT-safe — uses IJsonModel directly):
((IJsonModel<ResponseError>)errorValue).Write(writer, ModelReaderWriterOptions.Json);
// For deserialization:
var error = ((IJsonModel<ResponseError>)new ResponseError()).Create(ref reader, ModelReaderWriterOptions.Json);
Do not add the external type to your JsonSerializerContext — it won't source-generate for types you don't own. If the type doesn't implement IJsonModel<T>, write a custom JsonConverter<T> with manual Utf8JsonReader/Utf8JsonWriter logic and register it via [JsonSourceGenerationOptions] on your context.
Serialization libraries: Most reflection-based serializers (e.g., Newtonsoft.Json, XmlSerializer) are not AOT-compatible. Migrate to a source-generation-based serializer such as System.Text.Json with a JsonSerializerContext. If migration is not feasible, mark the serialization call site with [RequiresUnreferencedCode].
Shared projects / projitems: When source is shared between multiple projects via <Import>, annotations added to shared code affect ALL consuming projects. Verify that all consumers still build cleanly.
Limitations Conceptual: Understanding trimming How-to: trim compat
<IsAotCompatible> with TFM condition to .csproj#pragma warning disable or [UnconditionalSuppressMessage] used for any IL warningFrequently asked questions
Make .NET projects compatible with Native AOT and trimming by systematically resolving all IL trim/AOT analyzer warnings.
The source record exposes this install command: npx skills add https://github.com/dotnet/skills --skill "plugins/dotnet-upgrade/skills/dotnet-aot-compat". Inspect the command and pinned source before running it.
Static rules flagged read-files in the source; the page lists the matching lines and excerpts.
Alternatives
coreyhaines31/marketingskills
When the user wants to plan, design, or implement an A/B test or experiment, or build a growth experimentation program. Also use when the user mentions "A/B test," "split test," "experiment," "test this change," "variant copy," "multivariate test," "hypothesis," "should I test this," "which version is better," "test two versions," "statistical significance," "how long should I run this test," "growth experiments," "experiment velocity," "experiment backlog," "ICE score," "experimentation program
alirezarezvani/claude-skills
App Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklist
JasonColapietro/suede-creator-skills
Suede-owned experimentation discipline for hypotheses, sample sizing, test duration, significance, and repeatable experiment programs. Use when comparing variants, deciding whether a result is reliable, or building an experiment backlog and cadence. NOT FOR: analytics instrumentation (use suede-analytics), post-click conversion diagnosis (use suede-site-alchemy), or writing the variant copy itself (use suede-copy).
tenequm/skills
Decision validation and thinking frameworks for startup founders. Use when you need to pressure-test a decision, validate your next steps, think through strategic options, or sanity-check your approach. Triggers on phrases like "should I", "help me think through", "is this the right move", "validate my thinking", "what am I missing". Covers fundraising, customer development, runway management, prioritization, and crypto/web3 founder challenges.