Create prototype-backed API proposals for dotnet/runtime. Use when asked to draft an API proposal, write an api-suggestion issue, refine a vague API idea into a complete proposal, or improve a proposal marked api-needs-work. Covers the full pipeline from research through prototyping, ref source generation, and publishing. DO NOT USE FOR bug fixes, code review, performance benchmarking, or internal API changes that don't affect public surface area.
Create complete, terse, and empirically grounded API proposals for dotnet/runtime. The output should have a high chance of passing the API review process.
Prototype first, proposal second. Proposals without working prototypes are speculative — they get sent back because reviewers can't verify the design works. The prototype IS the evidence.
Use this skill when:
api-suggestion issue to refineTERSENESS: Proposals are reviewed live by humans during API review meetings who often lack prior context. Long text is counterproductive unless warranted by design complexity. Focus on WHAT problem and HOW to solve it.
Empirically grounded: Build and test a working prototype BEFORE writing the proposal. The prototype validates the design, surfaces edge cases, and produces the exact API surface via ref source generation.
Claims backed by evidence: Every motivating claim must have at least one concrete scenario. "This is useful" without showing who needs it and how they'd use it is the #1 reason proposals get sent back.
Context-driven depth: The amount of supporting text should be proportional to how much new information the proposal introduces — not just API surface size. A small API introducing a novel concept needs more justification than a large API adding async counterparts to existing sync methods.
These are failure modes that LLM agents hit repeatedly when drafting proposals. Check your output against this list before publishing.
GenerateReferenceAssemblySource output instead of curating it to show only the new/changed API surface. The proposal should be the edited API shape, not a build artifact.This skill has 6 phases. Each can run independently (e.g., "just draft the proposal from my existing prototype"). When running the full pipeline, execute in order.
Accept input in any form: issue URL, text description, API sketch, or vague idea.
If the input is an existing GitHub issue, read it in full and identify:
api-needs-work label is present)Identify the target: namespace, area label, affected types, target library under src/libraries/.
Assess novelty: Is this a well-understood pattern extension (async variant, new overload, casing option) or something introducing a novel concept? This determines the depth of the proposal.
Evaluate existing workarounds: Before proceeding, research what users can do TODAY without this API.
Search for prior proposals on the same topic:
api-suggestion, api-needs-work, or closed issues)api-needs-work? Why was it closed or stalled? Learn from that history to avoid repeating the same mistakesAsk clarifying questions if the proposal is too vague to prototype.
The skill contains baked-in examples and guidelines for writing good proposals (see references/proposal-examples.md and references/api-proposal-checklist.md). The agent does NOT need to search for api-approved issues as templates — the baked-in references are sufficient. Phase 0's search for related issues on the same topic is a separate concern and is still required.
What the agent DOES at runtime:
Read the Framework Design Guidelines digest at docs/coding-guidelines/framework-design-guidelines-digest.md. Validate that proposed names follow the conventions.
Read existing APIs in the target namespace to ensure consistency:
TryX pattern, XAsync pattern, overload shapes)Read the reference documentation for updating ref source at docs/coding-guidelines/updating-ref-source.md.
Research existing usage of the APIs being modified or extended. This is especially important when the proposal changes existing API behavior, adds overloads that could cause ambiguity, or has compatibility implications.
If the user already has a prototype, ask for the published branch link and/or commit hash. Skip steps 1–3 below, but still run all steps in Prototype Validation against the provided branch/commit before proceeding to Phase 3.
Create a new branch: api-proposal/<short-name>. The prototype must be kept as a single commit on this branch.
Implement the API surface with:
#if guards for TFM-specific APIsWrite comprehensive tests:
[Theory] with [InlineData]/[MemberData] where applicableSearch dotnet/runtime for adoption sites. Before writing any proposal text, systematically search the entire local repo (grep/ripgrep) for every place the new API could or should be used. This serves two purposes: validating that the API shape works in diverse real-world contexts, and producing the adoption catalog required in the proposal (see Phase 4, section 6).
What to search for:
How to catalog results:
Apply the new API at representative sites. From the catalog above, convert a diverse, representative set of adoption sites in the prototype commit. Prioritize sites that exercise different aspects of the API (different overloads, edge cases, interaction with other APIs). The remaining "Candidate" sites are listed in the proposal but left unconverted.
Prerequisite: Follow the build and test workflow in
copilot-instructions.md— complete the baseline build, configure the environment, and use the component-specific workflow for the target library. All build and test steps below assume the baseline build has already succeeded.
Step 1: Build and test
Build the src and test projects, then run all tests for the target library using the workflow described in copilot-instructions.md. All tests must pass with zero failures.
Building the test project separately is critical for detecting source breaking changes that ApiCompat won't catch:
Step 2: Check TFM compatibility
Inspect the library's .csproj for TargetFrameworks. If it ships netstandard2.0 or net462 artifacts:
$(NetCoreAppCurrent)#if guards where types like DateOnly, IParsable<T> restrict parts of the surface to .NET CoreStep 3: Generate reference assembly source
cd src/libraries/<LibraryName>/src
dotnet msbuild /t:GenerateReferenceAssemblySource
For System.Runtime, use dotnet build --no-incremental /t:GenerateReferenceAssemblySource.
This:
ref/ folder changes must be committed as part of the prototypeThe flow is: vague input → working prototype → extract exact API surface from ref source → write the proposal. The prototype comes BEFORE the exact API proposal.
Invoke the code-review skill against the prototype diff.
All errors and warnings must be fixed before proceeding to the draft phase.
If the API change could affect performance (hot paths, allocations, new collection types), suggest running the performance-benchmark skill.
Re-run tests after any review-driven changes to confirm nothing regressed.
Core principle: TERSENESS. Focus on WHAT problem and HOW to solve it. Do not generate long text unless the design complexity warrants it.
Write the proposal matching the spirit of the issue template. Skip inapplicable fields rather than filling them with "N/A".
1. Background and motivation
ToHashSet?"), note it here or in Alternative Designs.2. API Proposal
The exact API surface, extracted from the GenerateReferenceAssemblySource output:
namespace System.Collections.Generic;
public class PriorityQueue<TElement, TPriority>
{
public PriorityQueue();
public PriorityQueue(IComparer<TPriority>? comparer);
public int Count { get; }
public void Enqueue(TElement element, TPriority priority);
public TElement Dequeue();
// ...
}
csharp blocks when the proposal only adds new members and doesn't need to show existing APIs for context. Mark the containing type partial to emphasize it has other public members:namespace System.Text.Json;
public partial class JsonNamingPolicy
{
public static JsonNamingPolicy SnakeLowerCase { get; }
public static JsonNamingPolicy SnakeUpperCase { get; }
}
When existing members ARE needed for context (e.g., to show sibling overloads), comment out those existing members in the snippet and add a // EXISTING marker immediately above them. This makes it easy for the meeting chair to delete them when posting final approval notes:
namespace System.Text.Json;
public partial class JsonNamingPolicy
{
// EXISTING
// public static JsonNamingPolicy CamelCase { get; }
public static JsonNamingPolicy SnakeLowerCase { get; }
public static JsonNamingPolicy SnakeUpperCase { get; }
}
Rules:
Include the prototype link at the bottom of this section: "Prototype: https://github.com/<owner>/<repo>/commit/<sha>"
3. API Usage
Realistic, compilable code examples demonstrating the primary scenarios. Number and depth should match the novelty of the API, not just its size. A simple new overload may need one example; a new collection type may need several showing different use patterns.
4. Alternative Designs
The agent has the burden of proof when claiming no viable alternatives exist. Show that alternatives were genuinely considered and explain why the proposed design is preferred. For nontrivial design decisions where reasonable alternatives exist, briefly explain the reasoning here. List format works well:
IEnumerable because elements cannot be efficiently enumerated in priority order"Include any unresolved design questions with tentative answers. Surfacing uncertainty is a feature, not a weakness. Example from PriorityQueue:
KeyValuePair instead of tuples? — We will use tuple types."Omit this section entirely for straightforward additions with no meaningful alternatives.
5. Risks
The agent has the burden of proof when claiming absence of risks. Evaluate:
Write "No response" if there are genuinely no risks, matching the convention used in real api-approved issues. Do not inflate this section for straightforward additions.
6. Usage in dotnet/runtime
Include the full adoption catalog produced during Phase 2, step 4. This section demonstrates that the API is broadly useful across the runtime codebase and helps area owners discover conversion opportunities in their code.
Format as a table or grouped list:
#### Updated in prototype
| File | Description |
|------|-------------|
| `src/libraries/System.Linq/src/System/Linq/Where.cs` | Replaced manual null-check + throw with `ArgumentNullException.ThrowIfNull` |
| `src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Queue.cs` | Replaced bounds-check boilerplate |
#### Candidates for follow-up
| File | Description | Why deferred |
|------|-------------|--------------|
| `src/coreclr/nativeaot/...` | Same pattern as above | Different area owner |
| `src/mono/...` | Uses equivalent Mono-specific helper | Needs area-owner review |
Present the complete draft to the user for review. Iterate based on feedback before publishing.
AI-generated content disclosure: When posting any content to GitHub (issue body, PR body, comments) under a user's credentials — i.e., the account is not a dedicated "copilot" or "bot" account/app — prepend a concise, visible note (e.g. a
> [!NOTE]alert) indicating the content was AI/Copilot-generated. This applies to the initial proposal, iteration updates, and any follow-up comments posted on the user's behalf. Skip this if the user explicitly asks you to omit it.
Commit prototype changes and push the branch to the user's fork (default) or ask for an alternative remote. Capture the commit URL for inclusion in the proposal (e.g., https://github.com/<owner>/<repo>/commit/<sha>).
If the agent cannot prompt the user for input (e.g., running as Copilot Coding Agent), automatically post the API proposal as a comment on the associated pull request:
gh pr comment <pr-number> --body-file proposal.md
Skip the interactive options below.
Present the user with the following options. Which options appear depends on context:
Note: Always write the proposal text to a temporary file (e.g.,
proposal.md) and use--body-fileinstead of--bodyto avoid shell quoting/escaping issues with multi-line text.
Post as comment on existing issue/PR — Only offer this when the user explicitly referenced an issue or PR in their original prompt.
gh issue comment <number> --body-file proposal.md
# or
gh pr comment <number> --body-file proposal.md
Create a new issue — Always offer this option.
gh issue create --label api-suggestion --title "[API Proposal]: <title>" --body-file proposal.md
No area label — repo automation handles that.
Create a new draft PR with proposal in OP — Always offer this option.
gh pr create --draft --title "[API Proposal]: <title>" --body-file proposal.md
Include related issue links in the body for all options.
When the user provides feedback after a proposal has been published and the prototype needs to change:
Apply the requested changes, then re-run the full Phase 2 validation (build, test, TFM compatibility, GenerateReferenceAssemblySource) and Phase 3 review (code-review skill) before proceeding.
Before committing, ask the user which push strategy to use:
api-proposal/<short-name>-v2, -v3, etc.) with a single amended commit. The original branch is preserved.After pushing, automatically edit the published proposal text (comment, issue, or PR body) to:
### Iteration History
- **v1**: https://github.com/<owner>/<repo>/commit/<sha1> — initial prototype
- **v2**: https://github.com/<owner>/<repo>/commit/<sha2> — added `comparer` overloads