[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6PSFZGRB76V2ZEH2Z1S3Z5F":3,"skill-same:01M6PSFZGRB76V2ZEH2Z1S3Z5F":87},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":45,"commitSha":46,"skillDirectory":47,"forksCount":48,"repositoryHtmlUrl":49,"repositoryHomepageUrl":50,"ownerHtmlUrl":51,"ownerBlogUrl":52,"ownerDescription":53,"updatedAt":54,"install":55,"resources":68},"01M6PSFZGRB76V2ZEH2Z1S3Z5F","dotnet-breaking-changes",null,"Use before changing .NET\u002FC# code whose callers you do not control: a NuGet library, or a repo with PublicAPI.Shipped\u002FUnshipped.txt, ApiCompat, multiple TargetFrameworks, InternalsVisibleTo, or generated (.g.cs) partials. Green builds miss silent breaks from optional parameters, parameter names, sealing, const values, nullability, and #if. USE FOR: \"without breaking existing callers\"; adding a public method, overload, optional parameter, or interface member to a shipped library; renaming a public method or an internal helper a friend assembly uses; consolidating duplicated logic; changing a signature, nullability, default, or attribute; adding a target framework; RS0016\u002FRS0017 or ApiCompat errors; \"is this PR breaking?\". For a rename, extract, or merge, also invoke csharp-refactoring. DO NOT USE FOR: an executable whose callers all live in one project; renaming a local variable or private member, even in a shipped library; formatting; framework\u002FSDK\u002FNuGet upgrades (use dotnet-upgrade).","managedcode\u002Fdotnet-skills","managedcode","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F78253589?v=4","01M6DYDJR3EG8KB5G4PC65XCTT","Managed Code","01M6DYDJX0ZT432N04NYJW89DN",512,"MIT","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet\u002Fskills\u002Fdotnet-breaking-changes\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"dotnet breaking changes","changes","dotnet","green","invoke","errors","you","interface","live","project","local","upgrade","rename","default","assembly","generated","refactoring","library","code","merge","2026-11-10 10:12:30",false,"---\nname: dotnet-breaking-changes\ndescription: >\n  Use before changing .NET\u002FC# code whose callers you do not control: a NuGet library, or a repo with\n  PublicAPI.Shipped\u002FUnshipped.txt, ApiCompat, multiple TargetFrameworks, InternalsVisibleTo, or\n  generated (.g.cs) partials. Green builds miss silent breaks from optional parameters, parameter\n  names, sealing, const values, nullability, and #if.\n  USE FOR: \"without breaking existing callers\"; adding a public method, overload, optional\n  parameter, or interface member to a shipped library; renaming a public method or an internal\n  helper a friend assembly uses; consolidating duplicated logic; changing a signature,\n  nullability, default, or attribute; adding a target framework; RS0016\u002FRS0017 or ApiCompat errors;\n  \"is this PR breaking?\". For a rename, extract, or merge, also invoke csharp-refactoring.\n  DO NOT USE FOR: an executable whose callers all live in one project; renaming a local variable\n  or private member, even in a shipped library; formatting; framework\u002FSDK\u002FNuGet upgrades\n  (use dotnet-upgrade).\nlicense: MIT\n---\n\n# .NET breaking changes: the contract is wider than the file\n\nWhen you edit .NET code, the observable contract is usually **larger than the snippet in front of you**.\nA change that compiles and keeps tests green can still break a downstream consumer, another target\nframework, a friend assembly, or regenerate away on the next build. This applies to a feature or a bug\nfix as much as a refactor, and to **additions** — a new public member, overload, or target framework — as\nmuch as removals: added surface is a permanent contract obligation, and added behavior on a multi-targeted\nor partial type must stay correct on every target and survive the next regeneration. (For the\nbehavior-preserving refactoring *process*, use the `csharp-refactoring` skill; this skill is the\ncompatibility knowledge it — and any other change — taps into.)\n\n## Inspect first — find which surfaces this repo actually has\n\nThe most valuable move is to **look before you leap**: find which hidden surfaces exist here, then check\nonly those. Don't recite these or assume — the markers present dictate the plan; markers absent tell you a\nsurface is not in play.\n\n```bash\n# Public-API gate (which one? they are NOT interchangeable)\ngit ls-files \"**\u002FPublicAPI.Shipped.txt\" \"**\u002FPublicAPI.Unshipped.txt\"                 # PublicApiAnalyzers\ngrep -rl \"EnablePackageValidation\\|ApiCompat\" --include=*.props --include=*.targets --include=*.csproj .\n# Multi-targeting and conditional code\ngrep -rl \"\u003CTargetFrameworks>\" --include=*.csproj --include=*.props . ; grep -rn \"#if \" --include=*.cs .\n# Generated \u002F partial code\ngit ls-files \"*.g.cs\" \"*.generated.cs\" ; grep -rln \"partial class\\|partial record\\|partial struct\" --include=*.cs .\n# Friend assemblies\ngrep -rn \"InternalsVisibleTo\" --include=*.cs --include=*.csproj --include=*.props .\n```\n\nThen read only the reference(s) for the surfaces you actually found. For every `InternalsVisibleTo`\nname, check whether that project is in the solution: a friend **not** built here is an external\nconsumer of every internal it can see, exactly like a public caller, and a green solution build proves\nnothing about it.\n\nBefore editing, confirm the request's premise is true in the code. For \"consolidate\", \"deduplicate\", or\n\"add X\" requests, trace the actual calls: if one type already delegates to the other, or the member\nalready exists, the work is done — change nothing and report the delegating call (see Stop and escalate).\n\n## The hidden surfaces (depth in references\u002F)\n\nPublic API (1) is the **default** surface to check on any library edit; the other three are\n**conditional** — pursue them only when the inspect-first markers above show they are in play.\n\n1. **Public API.** Renaming\u002Fmoving\u002Fremoving\u002Fre-signing a public member — or changing nullability, a\n   generic constraint, or a trimming\u002FAOT attribute — is a breaking change a green test run will not catch.\n   Repos gate this **two non-interchangeable ways**: source-level (`PublicApiAnalyzers` + `PublicAPI.*.txt`,\n   which you maintain) and binary\u002Fpackage-level (`ApiCompat` \u002F `\u003CEnablePackageValidation>`). Respect the\n   gate that exists; if none exists, review the surface by hand and flag it — do **not** bolt on analyzer\n   infrastructure as a side effect. Move a public type via a `[TypeForwardedTo]` forwarder; a *rename*\n   needs an `[Obsolete]` shim, not a forwarder. See `references\u002Fpublic-api.md`.\n\n2. **Multi-targeting and `#if`.** Code that multi-targets (`\u003CTargetFrameworks>`) or compiles conditionally\n   (`#if NET8_0_OR_GREATER`, platform\u002FCoreCLR\u002FMono\u002FNativeAOT branches) can build for the target your editor\n   shows and break one it never invoked. Inspect every branch — including inactive ones — preserve\n   intentional divergence, and re-gate each TFM\u002FRID. See `references\u002Fmulti-targeting.md`.\n\n3. **Source-generated and partial code.** A type is often `partial` across several files, and part may be\n   **generated** (source generators, Razor, `*.g.cs`). Include every partial declaration; treat generated\n   files as derived and change the generator input\u002Ftemplate unless the repo checks the output in as source.\n   See `references\u002Fsource-generation.md`.\n\n4. **InternalsVisibleTo.** `internal` is not private across a solution: test and friend assemblies bind to\n   internals (and, when strong-named, to a public key). An internal rename\u002Fmove\u002Fremoval can break a\n   consumer with no reference in the declaring project. A friend built outside this repo cannot be\n   updated, so keep the old internal name as an `[Obsolete]` forwarder. See `references\u002Finternals-visible-to.md`.\n\n## Decisions a green build will not make for you\n\nEach row below compiles every caller and passes every test, yet breaks a consumer. Do not stop at a\ngreen build or test run. Use the safe shape instead.\n\nWhen the safe shape departs from the literal request — an overload instead of the requested optional\nparameter, a forwarder kept instead of a deleted or renamed member, or no change at all — say so in your\nfinal answer in one or two sentences: what you did instead, and which consumer it would have broken\n(e.g. \"already-compiled callers would throw `MissingMethodException`\"). A silent substitution reads as\nignoring the request.\n\nTreat that substitution as one atomic deliverable: implement the safe shape, preserve the old contract\nand shipped baseline, test both old and new entry points, and explain the compatibility reason. After the\nlast edit, inspect the diff or reopen the implementation file before claiming success. A green test run\ndoes not prove a failed patch applied when the unchanged fixture already passes those tests.\n\nDo not use labels such as \"for binary compatibility\" as the whole explanation. Name the exact old\nentry point that remains, the consumer that still binds to it, and the concrete failure avoided. For\nexample: \"Kept `Parse(string?, int)` unchanged because already-compiled consumers still request that\nexact method and would throw `MissingMethodException` if it disappeared.\" For source-only breaks, name\nthe affected syntax (`raw:` named arguments, derivation from a newly sealed class, or nullable callers)\ninstead. This causal sentence is part of the implementation, not optional release-note polish.\n\n| Tempting change | Why it breaks | Safe shape |\n|-----------------|---------------|------------|\n| Add an optional parameter to a shipped method (`Foo(a)` → `Foo(a, b = null)`) | **Binary break**: the old signature is gone, so already-compiled consumers throw `MissingMethodException` until they recompile | Keep `Foo(a)` unchanged and add a new overload `Foo(a, b)` with `b` required on that overload; omission continues to select the old method. Record only the new overload |\n| Fix RS0017 by editing or deleting a line in `PublicAPI.Shipped.txt` | Shipped lines are the released contract; rewriting one hides the break the gate exists to report | Leave shipped lines alone. Restore the shipped member and record new surface in `PublicAPI.Unshipped.txt` |\n| Add a member to a shipped public interface | Every existing implementer, including ones in other packages, stops compiling | Add a default interface implementation (requires runtime support: not on `netstandard2.0` or .NET Framework targets), or add a new interface. A default member is callable only through the interface, so also add a public member on any concrete type that must expose it, and record both |\n| Rename a shipped member | Source and binary break | Add the new name, keep the old one as an `[Obsolete]` forwarder, and record only the new member |\n| Rename or remove an `internal` member when `InternalsVisibleTo` names an assembly not in the solution | That friend was compiled against the old name and cannot be updated from this repo; it fails at bind time | Add the new name, migrate in-repo callers, keep the old internal name as an `[Obsolete]` forwarder, keep the grant, and name the protected friend in the answer |\n| Rename a parameter of a shipped member | Callers passing it by name (`raw: x`) stop compiling and late-bound\u002F`dynamic` callers fail; positional callers in this repo hide it. PublicAPI analyzers report it as RS0016 + RS0017 | Keep the shipped name — no overload can carry a different name for the same signature. Leave it unchanged and edit nothing else; you may offer (not make) a clearer XML `\u003Cparam>` doc, or the rename at the next major version |\n| Seal a class, make a member non-virtual, or narrow a parameter's nullability | Derived types, overrides, or nullable callers break | Treat as a breaking change; escalate rather than edit |\n| Change a `const` value or an optional parameter's default | The old value is compiled into callers and persists until they recompile | Preserve the shipped constant\u002Fdefault; introduce a separately named member (which may be `static readonly`) or a new overload for new behavior. Replacing an existing `const` with `static readonly` is itself source-breaking |\n| Change a value under `#if` for one target | Only consumers of that target see it; tests edited to match will not catch it | Treat as a behavior change for that target and call it out |\n\nWhenever you kept an old entry point or departed from the literal request, the final answer must\ncontain this line, filled in — terse bullets such as \"preserved for binary consumers\" do not satisfy it:\n\n> **Compatibility:** Kept `\u003Cexact old signature>` unchanged because `\u003Cwho still binds to it>` would\n> `\u003Cconcrete failure>` if it changed. `\u003CWhat you did instead of the literal request, and why.>`\n\nBefore finishing a compatibility-preserving edit, make the delta easy to audit:\n\n1. Name the **new** entry point or behavior and the **exact old** entry point that remains.\n2. Name the in-repo caller, generated template, or friend assembly you migrated, plus any external\n   consumer you could not migrate and therefore protected with a forwarder.\n3. State which baseline stayed unchanged and which new surface was recorded.\n4. Report the exact compatibility check that exercised the old entry point as well as the new one.\n\nFor a no-change outcome, lead with `No files changed.` and identify the decisive evidence before any\ngeneral explanation: the existing delegation, shipped signature, parameter name, or nullability line.\n\nWhen asked **\"is this a breaking change?\"** or to review a diff, make no edits unless asked and answer\nin this shape, scaled to the diff:\n\n1. One verdict line first (e.g. \"Do not merge as-is: N changes break consumers\").\n2. One numbered item per consumer-visible change: what changed, **binary** (already-compiled\n   consumers fail), **source** (consumers fail to recompile), or **behavioral** (same signature,\n   different result, possibly on only one target), who breaks, **why CI stayed green** (a shipped\n   API line rewritten, a test edited to match, or a change the analyzer does not track, such as\n   `sealed` or a private `const`), and the safe shape. State each as fact or not at all: a\n   behavioral claim needs a concrete input whose old and new results differ; if unsure, probe both\n   versions with a one-line program — framework parsers already tolerate much (e.g. `bool.TryParse`\n   ignores surrounding whitespace), and a no-op rewrite is not a break.\n3. One closing \"Not breaking:\" line covering every remaining hunk with its reason (internal-only\n   edits, probed no-ops), instead of side notes inside the numbered items.\n\nCover only surfaces the diff touches; do not add trimming, AOT, or tooling commentary it does not raise.\n\n## Verify before reporting\n\nBuild and test **every** target framework (`dotnet test -f \u003Ctfm>` for each), not only the default. If\nthe project sets `EnablePackageValidation`, run `dotnet pack` — package validation runs only after\n`Pack`, never on build. Report exactly which commands ran and their result.\n\n## Stop and escalate (do not silently proceed)\n\n- The requested change is **already in place** — e.g. the \"duplicated\" code already delegates to one\n  implementation, or the overload already exists. Say so and change nothing; do not invent a\n  refactor to have something to show.\n- A **public\u002Fshipped API** would change and you cannot verify compatibility — no shim\u002Fforwarder path and\n  no PublicApiAnalyzers\u002FApiCompat\u002Fpackage-validation gate to catch a break.\n- The edit changes an **observable annotation** (nullability, `[DynamicallyAccessedMembers]`,\n  `[RequiresUnreferencedCode]`, generic constraints) on a public member.\n- The change can be made consistent across **some but not all** target frameworks or platforms.\n- The only way to apply it is editing **generated output** that the next build will overwrite.\n\nWhen you change nothing (the request is already done or has no safe shape), a bare \"no\" looks like\nignoring the request. Make the final answer show the work, in this order: \"No files changed.\" and why;\nthe evidence that decided it (the shipped `PublicAPI.Shipped.txt` line, or the call that already\ndelegates, with its file); who would break, or where the single implementation already lives; what the\nuser *can* do instead, if anything; and the validation you ran.\n\n## Reference files\n\nLoad a reference only for a surface the inspect-first step actually found:\n\n- **[references\u002Fpublic-api.md](references\u002Fpublic-api.md)** — the two API gates and why they are not\n  interchangeable, nullable\u002Ftrimming\u002FAOT as observable API, `[Obsolete]` shims and type-forwarders,\n  suppression baselines.\n- **[references\u002Fmulti-targeting.md](references\u002Fmulti-targeting.md)** — TFMs, `#if` and platform symbols,\n  RIDs, and how to inspect and re-gate every target.\n- **[references\u002Fsource-generation.md](references\u002Fsource-generation.md)** — partial types, generated output\n  vs checked-in source, editing the generator input. (See also `dotnet-msbuild\u002Fincluding-generated-files`.)\n- **[references\u002Finternals-visible-to.md](references\u002Finternals-visible-to.md)** — friend\u002Ftest assemblies\n  and strong-name keys.\n","# .NET breaking changes: the contract is wider than the file\n\nWhen you edit .NET code, the observable contract is usually **larger than the snippet in front of you**.\nA change that compiles and keeps tests green can still break a downstream consumer, another target\nframework, a friend assembly, or regenerate away on the next build. This applies to a feature or a bug\nfix as much as a refactor, and to **additions** — a new public member, overload, or target framework — as\nmuch as removals: added surface is a permanent contract obligation, and added behavior on a multi-targeted\nor partial type must stay correct on every target and survive the next regeneration. (For the\nbehavior-preserving refactoring *process*, use the `csharp-refactoring` skill; this skill is the\ncompatibility knowledge it — and any other change — taps into.)\n\n## Inspect first — find which surfaces this repo actually has\n\nThe most valuable move is to **look before you leap**: find which hidden surfaces exist here, then check\nonly those. Don't recite these or assume — the markers present dictate the plan; markers absent tell you a\nsurface is not in play.\n\n```bash\n# Public-API gate (which one? they are NOT interchangeable)\ngit ls-files \"**\u002FPublicAPI.Shipped.txt\" \"**\u002FPublicAPI.Unshipped.txt\"                 # PublicApiAnalyzers\ngrep -rl \"EnablePackageValidation\\|ApiCompat\" --include=*.props --include=*.targets --include=*.csproj .\n# Multi-targeting and conditional code\ngrep -rl \"\u003CTargetFrameworks>\" --include=*.csproj --include=*.props . ; grep -rn \"#if \" --include=*.cs .\n# Generated \u002F partial code\ngit ls-files \"*.g.cs\" \"*.generated.cs\" ; grep -rln \"partial class\\|partial record\\|partial struct\" --include=*.cs .\n# Friend assemblies\ngrep -rn \"InternalsVisibleTo\" --include=*.cs --include=*.csproj --include=*.props .\n```\n\nThen read only the reference(s) for the surfaces you actually found. For every `InternalsVisibleTo`\nname, check whether that project is in the solution: a friend **not** built here is an external\nconsumer of every internal it can see, exactly like a public caller, and a green solution build proves\nnothing about it.\n\nBefore editing, confirm the request's premise is true in the code. For \"consolidate\", \"deduplicate\", or\n\"add X\" requests, trace the actual calls: if one type already delegates to the other, or the member\nalready exists, the work is done — change nothing and report the delegating call (see Stop and escalate).\n\n## The hidden surfaces (depth in references\u002F)\n\nPublic API (1) is the **default** surface to check on any library edit; the other three are\n**conditional** — pursue them only when the inspect-first markers above show they are in play.\n\n1. **Public API.** Renaming\u002Fmoving\u002Fremoving\u002Fre-signing a public member — or changing nullability, a\n   generic constraint, or a trimming\u002FAOT attribute — is a breaking change a green test run will not catch.\n   Repos gate this **two non-interchangeable ways**: source-level (`PublicApiAnalyzers` + `PublicAPI.*.txt`,\n   which you maintain) and binary\u002Fpackage-level (`ApiCompat` \u002F `\u003CEnablePackageValidation>`). Respect the\n   gate that exists; if none exists, review the surface by hand and flag it — do **not** bolt on analyzer\n   infrastructure as a side effect. Move a public type via a `[TypeForwardedTo]` forwarder; a *rename*\n   needs an `[Obsolete]` shim, not a forwarder. See `references\u002Fpublic-api.md`.\n\n2. **Multi-targeting and `#if`.** Code that multi-targets (`\u003CTargetFrameworks>`) or compiles conditionally\n   (`#if NET8_0_OR_GREATER`, platform\u002FCoreCLR\u002FMono\u002FNativeAOT branches) can build for the target your editor\n   shows and break one it never invoked. Inspect every branch — including inactive ones — preserve\n   intentional divergence, and re-gate each TFM\u002FRID. See `references\u002Fmulti-targeting.md`.\n\n3. **Source-generated and partial code.** A type is often `partial` across several files, and part may be\n   **generated** (source generators, Razor, `*.g.cs`). Include every partial declaration; treat generated\n   files as derived and change the generator input\u002Ftemplate unless the repo checks the output in as source.\n   See `references\u002Fsource-generation.md`.\n\n4. **InternalsVisibleTo.** `internal` is not private across a solution: test and friend assemblies bind to\n   internals (and, when strong-named, to a public key). An internal rename\u002Fmove\u002Fremoval can break a\n   consumer with no reference in the declaring project. A friend built outside this repo cannot be\n   updated, so keep the old internal name as an `[Obsolete]` forwarder. See `references\u002Finternals-visible-to.md`.\n\n## Decisions a green build will not make for you\n\nEach row below compiles every caller and passes every test, yet breaks a consumer. Do not stop at a\ngreen build or test run. Use the safe shape instead.\n\nWhen the safe shape departs from the literal request — an overload instead of the requested optional\nparameter, a forwarder kept instead of a deleted or renamed member, or no change at all — say so in your\nfinal answer in one or two sentences: what you did instead, and which consumer it would have broken\n(e.g. \"already-compiled callers would throw `MissingMethodException`\"). A silent substitution reads as\nignoring the request.\n\nTreat that substitution as one atomic deliverable: implement the safe shape, preserve the old contract\nand shipped baseline, test both old and new entry points, and explain the compatibility reason. After the\nlast edit, inspect the diff or reopen the implementation file before claiming success. A green test run\ndoes not prove a failed patch applied when the unchanged fixture already passes those tests.\n\nDo not use labels such as \"for binary compatibility\" as the whole explanation. Name the exact old\nentry point that remains, the consumer that still binds to it, and the concrete failure avoided. For\nexample: \"Kept `Parse(string?, int)` unchanged because already-compiled consumers still request that\nexact method and would throw `MissingMethodException` if it disappeared.\" For source-only breaks, name\nthe affected syntax (`raw:` named arguments, derivation from a newly sealed class, or nullable callers)\ninstead. This causal sentence is part of the implementation, not optional release-note polish.\n\n| Tempting change | Why it breaks | Safe shape |\n|-----------------|---------------|------------|\n| Add an optional parameter to a shipped method (`Foo(a)` → `Foo(a, b = null)`) | **Binary break**: the old signature is gone, so already-compiled consumers throw `MissingMethodException` until they recompile | Keep `Foo(a)` unchanged and add a new overload `Foo(a, b)` with `b` required on that overload; omission continues to select the old method. Record only the new overload |\n| Fix RS0017 by editing or deleting a line in `PublicAPI.Shipped.txt` | Shipped lines are the released contract; rewriting one hides the break the gate exists to report | Leave shipped lines alone. Restore the shipped member and record new surface in `PublicAPI.Unshipped.txt` |\n| Add a member to a shipped public interface | Every existing implementer, including ones in other packages, stops compiling | Add a default interface implementation (requires runtime support: not on `netstandard2.0` or .NET Framework targets), or add a new interface. A default member is callable only through the interface, so also add a public member on any concrete type that must expose it, and record both |\n| Rename a shipped member | Source and binary break | Add the new name, keep the old one as an `[Obsolete]` forwarder, and record only the new member |\n| Rename or remove an `internal` member when `InternalsVisibleTo` names an assembly not in the solution | That friend was compiled against the old name and cannot be updated from this repo; it fails at bind time | Add the new name, migrate in-repo callers, keep the old internal name as an `[Obsolete]` forwarder, keep the grant, and name the protected friend in the answer |\n| Rename a parameter of a shipped member | Callers passing it by name (`raw: x`) stop compiling and late-bound\u002F`dynamic` callers fail; positional callers in this repo hide it. PublicAPI analyzers report it as RS0016 + RS0017 | Keep the shipped name — no overload can carry a different name for the same signature. Leave it unchanged and edit nothing else; you may offer (not make) a clearer XML `\u003Cparam>` doc, or the rename at the next major version |\n| Seal a class, make a member non-virtual, or narrow a parameter's nullability | Derived types, overrides, or nullable callers break | Treat as a breaking change; escalate rather than edit |\n| Change a `const` value or an optional parameter's default | The old value is compiled into callers and persists until they recompile | Preserve the shipped constant\u002Fdefault; introduce a separately named member (which may be `static readonly`) or a new overload for new behavior. Replacing an existing `const` with `static readonly` is itself source-breaking |\n| Change a value under `#if` for one target | Only consumers of that target see it; tests edited to match will not catch it | Treat as a behavior change for that target and call it out |\n\nWhenever you kept an old entry point or departed from the literal request, the final answer must\ncontain this line, filled in — terse bullets such as \"preserved for binary consumers\" do not satisfy it:\n\n> **Compatibility:** Kept `\u003Cexact old signature>` unchanged because `\u003Cwho still binds to it>` would\n> `\u003Cconcrete failure>` if it changed. `\u003CWhat you did instead of the literal request, and why.>`\n\nBefore finishing a compatibility-preserving edit, make the delta easy to audit:\n\n1. Name the **new** entry point or behavior and the **exact old** entry point that remains.\n2. Name the in-repo caller, generated template, or friend assembly you migrated, plus any external\n   consumer you could not migrate and therefore protected with a forwarder.\n3. State which baseline stayed unchanged and which new surface was recorded.\n4. Report the exact compatibility check that exercised the old entry point as well as the new one.\n\nFor a no-change outcome, lead with `No files changed.` and identify the decisive evidence before any\ngeneral explanation: the existing delegation, shipped signature, parameter name, or nullability line.\n\nWhen asked **\"is this a breaking change?\"** or to review a diff, make no edits unless asked and answer\nin this shape, scaled to the diff:\n\n1. One verdict line first (e.g. \"Do not merge as-is: N changes break consumers\").\n2. One numbered item per consumer-visible change: what changed, **binary** (already-compiled\n   consumers fail), **source** (consumers fail to recompile), or **behavioral** (same signature,\n   different result, possibly on only one target), who breaks, **why CI stayed green** (a shipped\n   API line rewritten, a test edited to match, or a change the analyzer does not track, such as\n   `sealed` or a private `const`), and the safe shape. State each as fact or not at all: a\n   behavioral claim needs a concrete input whose old and new results differ; if unsure, probe both\n   versions with a one-line program — framework parsers already tolerate much (e.g. `bool.TryParse`\n   ignores surrounding whitespace), and a no-op rewrite is not a break.\n3. One closing \"Not breaking:\" line covering every remaining hunk with its reason (internal-only\n   edits, probed no-ops), instead of side notes inside the numbered items.\n\nCover only surfaces the diff touches; do not add trimming, AOT, or tooling commentary it does not raise.\n\n## Verify before reporting\n\nBuild and test **every** target framework (`dotnet test -f \u003Ctfm>` for each), not only the default. If\nthe project sets `EnablePackageValidation`, run `dotnet pack` — package validation runs only after\n`Pack`, never on build. Report exactly which commands ran and their result.\n\n## Stop and escalate (do not silently proceed)\n\n- The requested change is **already in place** — e.g. the \"duplicated\" code already delegates to one\n  implementation, or the overload already exists. Say so and change nothing; do not invent a\n  refactor to have something to show.\n- A **public\u002Fshipped API** would change and you cannot verify compatibility — no shim\u002Fforwarder path and\n  no PublicApiAnalyzers\u002FApiCompat\u002Fpackage-validation gate to catch a break.\n- The edit changes an **observable annotation** (nullability, `[DynamicallyAccessedMembers]`,\n  `[RequiresUnreferencedCode]`, generic constraints) on a public member.\n- The change can be made consistent across **some but not all** target frameworks or platforms.\n- The only way to apply it is editing **generated output** that the next build will overwrite.\n\nWhen you change nothing (the request is already done or has no safe shape), a bare \"no\" looks like\nignoring the request. Make the final answer show the work, in this order: \"No files changed.\" and why;\nthe evidence that decided it (the shipped `PublicAPI.Shipped.txt` line, or the call that already\ndelegates, with its file); who would break, or where the single implementation already lives; what the\nuser *can* do instead, if anything; and the validation you ran.\n\n## Reference files\n\nLoad a reference only for a surface the inspect-first step actually found:\n\n- **[references\u002Fpublic-api.md](references\u002Fpublic-api.md)** — the two API gates and why they are not\n  interchangeable, nullable\u002Ftrimming\u002FAOT as observable API, `[Obsolete]` shims and type-forwarders,\n  suppression baselines.\n- **[references\u002Fmulti-targeting.md](references\u002Fmulti-targeting.md)** — TFMs, `#if` and platform symbols,\n  RIDs, and how to inspect and re-gate every target.\n- **[references\u002Fsource-generation.md](references\u002Fsource-generation.md)** — partial types, generated output\n  vs checked-in source, editing the generator input. (See also `dotnet-msbuild\u002Fincluding-generated-files`.)\n- **[references\u002Finternals-visible-to.md](references\u002Finternals-visible-to.md)** — friend\u002Ftest assemblies\n  and strong-name keys.\n",{"name":5,"license":15,"description":44},"Use before changing .NET\u002FC# code whose callers you do not control: a NuGet library, or a repo with PublicAPI.Shipped\u002FUnshipped.txt, ApiCompat, multiple TargetFrameworks, InternalsVisibleTo, or generated (.g.cs) partials. Green builds miss silent breaks from optional parameters, parameter names, sealing, const values, nullability, and #if. USE FOR: \"without breaking existing callers\"; adding a public method, overload, optional parameter, or interface member to a shipped library; renaming a public method or an internal helper a friend assembly uses; consolidating duplicated logic; changing a signature, nullability, default, or attribute; adding a target framework; RS0016\u002FRS0017 or ApiCompat errors; \"is this PR breaking?\". For a rename, extract, or merge, also invoke csharp-refactoring. DO NOT USE FOR: an executable whose callers all live in one project; renaming a local variable or private member, even in a shipped library; formatting; framework\u002FSDK\u002FNuGet upgrades (use dotnet-upgrade).\n","main","6757c043b0250364c729a5799755c27397348bdc","external-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet\u002Fskills\u002Fdotnet-breaking-changes",41,"https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills","https:\u002F\u002Fskills.managed-code.com","https:\u002F\u002Fgithub.com\u002Fmanagedcode","https:\u002F\u002Fwww.managed-code.com","Open Source Community for .NET Developers: Reliable, Actively Maintained, and Community-Driven. Made in Ukraine 🇺🇦 with ❤️","2026-11-11 19:34:28",{"source":8,"skillName":5,"commands":56,"prompt":66,"zipAvailable":67},[57,60,63],{"tool":58,"command":59},"npx","npx skills add managedcode\u002Fdotnet-skills --skill dotnet-breaking-changes",{"tool":61,"command":62},"bunx","bunx skills add managedcode\u002Fdotnet-skills --skill dotnet-breaking-changes",{"tool":64,"command":65},"pnpm","pnpm dlx skills add managedcode\u002Fdotnet-skills --skill dotnet-breaking-changes","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet\u002Fskills\u002Fdotnet-breaking-changes\u002FSKILL.md",true,[69,75,79,83],{"path":70,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":73,"sourceHtmlUrl":74},"references\u002Finternals-visible-to.md","REFERENCE","参考文档",2327,"https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet\u002Fskills\u002Fdotnet-breaking-changes\u002Freferences\u002Finternals-visible-to.md",{"path":76,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":77,"sourceHtmlUrl":78},"references\u002Fmulti-targeting.md",2821,"https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet\u002Fskills\u002Fdotnet-breaking-changes\u002Freferences\u002Fmulti-targeting.md",{"path":80,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":81,"sourceHtmlUrl":82},"references\u002Fpublic-api.md",5657,"https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet\u002Fskills\u002Fdotnet-breaking-changes\u002Freferences\u002Fpublic-api.md",{"path":84,"resourceType":71,"resourceTypeDesc":72,"sizeBytes":85,"sourceHtmlUrl":86},"references\u002Fsource-generation.md",2003,"https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet\u002Fskills\u002Fdotnet-breaking-changes\u002Freferences\u002Fsource-generation.md",[88,124,148,180,190,216],{"skillNo":89,"name":90,"nameZh":6,"description":91,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":92,"categories":93,"keywords":104,"publishedAt":39},"01M6DYQE1Y9FHAQHEDTVFQ3447","xtwitter-scraper","Use XTwitterScraper when a .NET application needs typed access to Xquik's X automation REST API for tweet search, user lookup, posting workflows, extraction jobs, monitors, webhooks, or giveaway draws. USE FOR: integrating the XTwitterScraper NuGet package; reviewing Xquik API client usage; modeling API-key configuration, pagination, binary exports, raw responses, retries, and webhook\u002Fevent flows in .NET. DO NOT USE FOR: unrelated stacks; direct browser automation; generic social-media strategy; or code that does not call Xquik. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fcatalog\u002FLibraries\u002FXTwitterScraper\u002Fskills\u002Fxtwitter-scraper\u002FSKILL.md",[94,99],{"categoryNo":95,"categoryName":96,"taxonomyType":97,"taxonomyTypeDesc":98},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":100,"categoryName":101,"taxonomyType":102,"taxonomyTypeDesc":103},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[105,106,107,108,37,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123],"xtwitter scraper","browser automation","workflows","run","browser","search","repository","api","Media","api client","access","edit","unrelated","webhooks","files","social","commands","modeling","webhook",{"skillNo":125,"name":126,"nameZh":6,"description":127,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":128,"categories":129,"keywords":136,"publishedAt":39},"01M6DYQ9S4PE1H6G8MCH9T21HR","sep","Use Sep for high-performance separated-value parsing and writing in .NET, including delimiter inference, explicit parser\u002Fwriter options, and low-allocation row\u002Fcolumn workflows. USE FOR: delimited data needs are performance-sensitive and allocation-aware; project needs explicit control over separator inference, escaping, trimming, and header behavior;. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fcatalog\u002FLibraries\u002FSep\u002Fskills\u002Fsep\u002FSKILL.md",[130,133],{"categoryNo":131,"categoryName":132,"taxonomyType":97,"taxonomyTypeDesc":98},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":134,"categoryName":135,"taxonomyType":102,"taxonomyTypeDesc":103},"01JBSEEDCAT000000000000036","内容创作者",[126,137,138,107,139,108,111,140,141,142,116,143,117,119,144,145,146,28,121,147],"writing","performance","tasks","data","parsing","parser","context","do","writer","options","high-performance",{"skillNo":149,"name":150,"nameZh":6,"description":151,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":152,"categories":153,"keywords":154,"publishedAt":39},"01M6DZS2DNEPXCGGF6SSCW6G5V","webgpu-threejs-tsl","Comprehensive guide for developing WebGPU-enabled Three.js applications using TSL (Three.js Shading Language). Covers WebGPU renderer setup, TSL syntax and node materials, compute shaders, post-processing effects, and WGSL integration. Use this skill when working with Three.js WebGPU, TSL shaders, node materials, or GPU compute in Three.js.","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fwebgpu-claude-skill\u002Fskills\u002Fwebgpu-threejs-tsl\u002FSKILL.md",[],[155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179],"webgpu","threejs","webgpu threejs tsl","setup","shaders","gpu","integration","applications","js","node","syntax","guide","use","language","three","compute","post","processing","with","renderer","for","materials","wgsl","effects","when",{"skillNo":181,"name":182,"nameZh":6,"description":183,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":184,"categories":185,"keywords":186,"publishedAt":39},"01M6DZS0BQXW2B56A289XF1X76","roslynk","Use Roslynk for semantic inspection and edits of *.cs, *.razor and *.cshtml solution files.","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Froslynk\u002Fskills\u002Froslynk\u002FSKILL.md",[],[182,167,119,187,188,175,189],"semantic","razor","inspection",{"skillNo":191,"name":192,"nameZh":6,"description":193,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":194,"categories":195,"keywords":196,"publishedAt":39},"01M6DZRW6MEJCVW80K3S4XV60K","system-text-json-net11","Imperative guidance for the System.Text.Json APIs added in .NET 11: the built-in `JsonNamingPolicy.PascalCase` naming policy, and the strongly-typed generic `JsonSerializerOptions.GetTypeInfo` and `JsonSerializerOptions.TryGetTypeInfo` metadata accessors (generic overloads that return a typed `JsonTypeInfo`). USE ONLY when the user is targeting net11.0 or later and needs PascalCase JSON property or dictionary-key names without writing a custom naming policy, a strongly-typed generic `JsonTypeInfo` instead of the non-generic `JsonTypeInfo`, or a no-throw way to probe whether a type's serialization metadata is resolved. DO NOT USE when the target is earlier than net11.0, the requested behavior uses an established pre-net11 naming policy, or the user explicitly selected another JSON library.","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet11\u002Fskills\u002Fsystem-text-json-net11\u002FSKILL.md",[],[197,198,199,200,137,201,202,36,203,204,205,167,206,207,208,209,144,210,211,212,213,175,214,215,179],"system","json","text","system text json net11","policy","metadata","serialization","behavior","user","only","probe","guidance","later","type","dictionary","naming","targeting","custom","generic",{"skillNo":217,"name":218,"nameZh":6,"description":219,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":220,"categories":221,"keywords":224,"publishedAt":39},"01M6DZRRJP3S662NFSG1RSPQQ7","process-api-net11","Provides guidance on the new System.Diagnostics.Process APIs introduced in .NET 11: Process.Run, Process.RunAndCaptureText, Process.StartAndForget, Process.ReadAllText\u002FBytes\u002FLines, KillOnParentExit, InheritedHandles, and StartDetached. USE FOR: starting, orchestrating, or capturing output from external processes in applications targeting net11.0 or later. DO NOT USE FOR: applications targeting .NET 10 or earlier, or basic pre-.NET 11 Process.Start usage without new APIs.","https:\u002F\u002Fgithub.com\u002Fmanagedcode\u002Fdotnet-skills\u002Fblob\u002Fmain\u002Fexternal-sources\u002Fupstreams\u002Fdotnet-skills\u002Fplugins\u002Fdotnet11\u002Fskills\u002Fprocess-api-net11\u002FSKILL.md",[222,223],{"categoryNo":95,"categoryName":96,"taxonomyType":97,"taxonomyTypeDesc":98},{"categoryNo":100,"categoryName":101,"taxonomyType":102,"taxonomyTypeDesc":103},[112,225,226,227,108,228,162,197,167,208,209,144,229,230,231,232,213,175,233],"process","process api net11","diagnostics","start","basic","processes","output","usage","new"]