[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-dotnet-testability-obstacle":3,"mdc--3bfq59-key":37,"related-repo-dotnet-testability-obstacle":1184,"related-org-dotnet-testability-obstacle":1289},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":25,"repoUrl":26,"updatedAt":27,"license":28,"forks":29,"topics":30,"repo":32,"sourceUrl":35,"mdContent":36},"testability-obstacle","make C# code testable","Make C# ambient-dependent behavior testable and add deterministic tests. USE FOR: DateTime\u002FTask.Delay\u002FFile\u002FEnvironment\u002FGuid\u002FRandom, constructor injection for instance classes, preserving static APIs, nested override restore, parallel isolation, or no real I\u002FO. DO NOT USE FOR: audits, wrapper-only\u002Fbulk migration, or an existing injectable seam.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"dotnet",".NET (Microsoft)","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fdotnet.png",[12,16,19,22],{"name":13,"slug":14,"type":15},"C#","csharp","tag",{"name":17,"slug":18,"type":15},"Engineering","engineering",{"name":20,"slug":21,"type":15},"Testing","testing",{"name":23,"slug":24,"type":15},"Debugging","debugging",4576,"https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fskills","2026-08-14T04:51:57.063307","MIT",332,[31],"agent-skills",{"repoUrl":26,"stars":25,"forks":29,"topics":33,"description":34},[31],"Repository for skills to assist AI coding agents with .NET and C#","https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fskills\u002Ftree\u002FHEAD\u002Fplugins\u002Fdotnet-test\u002Fskills\u002Ftestability-obstacle","---\nname: testability-obstacle\ndescription: >-\n  Make C# ambient-dependent behavior testable and add deterministic\n  tests. USE FOR: DateTime\u002FTask.Delay\u002FFile\u002FEnvironment\u002FGuid\u002FRandom, constructor\n  injection for instance classes, preserving static APIs, nested override\n  restore, parallel isolation, or no real I\u002FO. DO NOT USE FOR: audits,\n  wrapper-only\u002Fbulk migration, or an existing injectable seam.\nlicense: MIT\n---\n\n# Resolve a Testability Obstacle\n\nIntroduce the smallest behavior-preserving seam needed to test a specific C#\nbehavior, then add deterministic tests that prove both the behavior and the seam.\nThe production edit is a means to the requested test, not an invitation to\nredesign adjacent code.\n\n## When to Use\n\n- A requested test would otherwise read\u002Fwrite the real filesystem.\n- Behavior depends on the current time, delay, random value, environment, console,\n  process, or another ambient dependency.\n- The user explicitly permits or requests a safe production seam.\n- Existing tests cannot control a dependency without process-global mutation.\n\n## When Not to Use\n\n- The dependency is already injected or passed as an argument. Write tests with\n  a fake through the existing seam using `code-testing-agent`.\n- The user wants a repository-wide testability audit. Use\n  `detect-static-dependencies`.\n- The user wants wrappers generated but not call sites\u002Ftests changed. Use\n  `generate-testability-wrappers`.\n- The user requests a broad mechanical migration. Use\n  `migrate-static-to-wrapper`, then generate tests separately.\n- The code is not C#\u002F.NET.\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Behavior to test | Yes | The method\u002Fworkflow and expected observable behavior |\n| Target scope | No | Discover the narrowest relevant file\u002Fproject when omitted |\n| Allowed production changes | No | Default to the minimum internal\u002Fconstructor seam |\n\n## Workflow\n\n### Step 1: Prove the obstacle\n\nRead the target production path and its existing tests. Identify the exact ambient\noperation preventing a deterministic test and the behavior that must remain\nunchanged. Do not run a repository-wide static scan for a single-class request.\n\nIf an adequate seam already exists, stop refactoring and use it. This skill adds\nno value when a fake can already be supplied.\n\n### Step 2: Select the smallest safe seam\n\nChoose by dependency and repository constraints:\n\n| Dependency | Preferred seam |\n|------------|----------------|\n| Current time \u002F timers | Inject `TimeProvider`; use `FakeTimeProvider` in tests |\n| Filesystem | Existing repository file abstraction; otherwise the smallest interface or `System.IO.Abstractions` when already used\u002Faccepted |\n| HTTP | Existing typed `HttpClient`\u002Fhandler or `IHttpClientFactory` seam |\n| Randomness | Inject `Random` or a minimal generator interface |\n| Environment\u002Fconsole\u002Fprocess | Minimal interface containing only members used by the target |\n\nThe scoped `AsyncLocal\u003CT>` rule applies to every static API that must retain its\npublic static shape — clocks, filesystem access, environment lookups, identity\ngeneration, and randomness. The scope captures and restores the previous value;\nnever implement `Dispose()` as an unconditional assignment to `null`.\n\nConstructor injection is the default for instance classes. Reuse the repository's\nDI and naming conventions, but do not add a DI container to a class library just\nto satisfy this workflow.\n\nFor a static class or a public API that cannot change, use a scoped ambient seam\nonly when constructor\u002Fparameter injection is impossible. The override must:\n\n- flow across `await` (`AsyncLocal\u003CT>`, not `[ThreadStatic]`);\n- return `IDisposable` and restore the previous value, including nested scopes;\n- default to the real production dependency;\n- avoid a process-global mutable fake that makes tests non-parallel.\n\nUse built-in fake-time-aware overloads instead of inventing an `IDelay` wrapper:\n\n| Ambient operation | Replacement |\n|-------------------|-------------|\n| `Task.Delay(delay, token)` | `Task.Delay(delay, timeProvider, token)` |\n| `new CancellationTokenSource(delay)` | `new CancellationTokenSource(delay, timeProvider)` |\n| `PeriodicTimer(period)` | `new PeriodicTimer(period, timeProvider)` when the target framework provides it |\n\nTest delayed behavior by starting the operation, proving it is incomplete,\nadvancing `FakeTimeProvider`, then awaiting it. Never wait for wall-clock time.\n\nFor a nested ambient override, disposing the inner scope must restore the outer\nvalue, not clear the slot. Capture the previous value per scope:\n\n```csharp\npublic static IDisposable OverrideClock(Func\u003CDateTimeOffset> clock)\n{\n    var previous = s_clock.Value;\n    s_clock.Value = clock;\n    return new Scope(() => s_clock.Value = previous);\n}\n```\n\nAdd tests for both nesting and parallel async flows; parallel-only tests do not\ncatch the common \"dispose sets null\" bug.\n\n### Step 3: Preserve behavior and API shape\n\nKeep the production change mechanical:\n\n- Wrap only members used by the target behavior.\n- Default implementations delegate directly to the original API.\n- Preserve exceptions, path handling, time zone, and `DateTime.Kind`.\n- Keep existing public signatures unless the user explicitly permits an API change.\n- Do not move business logic into the wrapper or fix unrelated production bugs.\n\nFor time replacements:\n\n- `DateTime.UtcNow` -> `timeProvider.GetUtcNow().UtcDateTime`\n- `DateTime.Now` -> `timeProvider.GetLocalNow().LocalDateTime`\n- `DateTimeOffset.UtcNow` -> `timeProvider.GetUtcNow()`\n- `DateTimeOffset.Now` -> `timeProvider.GetLocalNow()`\n\n### Step 4: Keep production defaults wired\n\nUpdate every composition root or constructor call affected by the seam. Production\nmust still use real time\u002Ffilesystem\u002Fetc. by default. If the project uses DI,\nregister the default implementation with the lifetime matching repository\nconventions. If it does not use DI, compose explicitly; do not introduce a\ncontainer.\n\nBuild the affected production project before writing tests. A compile failure here\nis a seam problem, not a test problem.\n\n### Step 5: Write deterministic tests\n\nUse the repository's existing test project. If none exists, invoke\n`scaffold-dotnet-test-project` first.\n\nTests must supply controlled dependencies:\n\n- fixed\u002Fadvanced time rather than wall-clock waiting;\n- an in-memory fake filesystem or hand-rolled fake rather than temp\u002Freal files;\n- no environment mutation, external process, console input, or network.\n\nAssert the requested business result and at least one interaction\u002Fstate observable\nthat proves the fake dependency drove the path. Include a production-default test\nonly when it can remain deterministic; never touch the real filesystem merely to\nprove the adapter delegates.\n\n### Step 6: Verify the complete path\n\nRun the affected production build, targeted test project, and repository-level\ntest command. Re-read the diff and confirm:\n\n1. every production change is required by the seam;\n2. no real ambient resource is used by the new tests;\n3. current-time semantics and public behavior are preserved;\n4. existing tests were not replaced or duplicated.\n\n## Output Contract\n\nProvide a compact `Requirement | Evidence` table. Cite the production seam,\nproduction default wiring, exact test names, and passing commands. If a package\nrestore or build blocks validation, report that blocker rather than claiming the\ntests pass.\n\n## Validation\n\n- [ ] The original obstacle was concrete and in the requested path.\n- [ ] An existing seam was reused when available.\n- [ ] The new abstraction exposes only members required by the target behavior.\n- [ ] Production defaults still delegate to the original dependency.\n- [ ] Time conversions preserve local\u002FUTC and `DateTime.Kind` semantics.\n- [ ] Static ambient overrides are async-safe, scoped, nested, and reversible.\n- [ ] New tests use fixed\u002Fin-memory dependencies and no real I\u002FO or wall clock.\n- [ ] Production build and targeted\u002Frepository tests pass.\n\n## Common Pitfalls\n\n| Pitfall | Corrective action |\n|---------|-------------------|\n| Refactoring before proving a blocker | Reuse an existing seam and write the test directly |\n| Wrapping an entire static API | Expose only members exercised by the target |\n| Converting `UtcNow` with `.DateTime` | Use `.UtcDateTime` to preserve `DateTimeKind.Utc` |\n| Mutable static fake shared by tests | Use constructor injection or a scoped `AsyncLocal\u003CT>` override |\n| Adding DI to a library with no container | Compose the dependency explicitly |\n| Using temp files as a shortcut | Supply an in-memory fake; the scenario requires no real I\u002FO |\n| Stopping after the refactor builds | Write and run the behavior tests that justified the seam |\n",{"data":38,"body":39},{"name":4,"description":6,"license":28},{"type":40,"children":41},"root",[42,51,57,64,89,95,154,160,246,252,259,264,269,275,280,414,442,447,452,506,519,606,618,623,689,694,700,705,740,745,813,819,824,829,835,848,853,871,876,882,887,911,917,930,936,1024,1030,1178],{"type":43,"tag":44,"props":45,"children":47},"element","h1",{"id":46},"resolve-a-testability-obstacle",[48],{"type":49,"value":50},"text","Resolve a Testability Obstacle",{"type":43,"tag":52,"props":53,"children":54},"p",{},[55],{"type":49,"value":56},"Introduce the smallest behavior-preserving seam needed to test a specific C#\nbehavior, then add deterministic tests that prove both the behavior and the seam.\nThe production edit is a means to the requested test, not an invitation to\nredesign adjacent code.",{"type":43,"tag":58,"props":59,"children":61},"h2",{"id":60},"when-to-use",[62],{"type":49,"value":63},"When to Use",{"type":43,"tag":65,"props":66,"children":67},"ul",{},[68,74,79,84],{"type":43,"tag":69,"props":70,"children":71},"li",{},[72],{"type":49,"value":73},"A requested test would otherwise read\u002Fwrite the real filesystem.",{"type":43,"tag":69,"props":75,"children":76},{},[77],{"type":49,"value":78},"Behavior depends on the current time, delay, random value, environment, console,\nprocess, or another ambient dependency.",{"type":43,"tag":69,"props":80,"children":81},{},[82],{"type":49,"value":83},"The user explicitly permits or requests a safe production seam.",{"type":43,"tag":69,"props":85,"children":86},{},[87],{"type":49,"value":88},"Existing tests cannot control a dependency without process-global mutation.",{"type":43,"tag":58,"props":90,"children":92},{"id":91},"when-not-to-use",[93],{"type":49,"value":94},"When Not to Use",{"type":43,"tag":65,"props":96,"children":97},{},[98,112,124,136,149],{"type":43,"tag":69,"props":99,"children":100},{},[101,103,110],{"type":49,"value":102},"The dependency is already injected or passed as an argument. Write tests with\na fake through the existing seam using ",{"type":43,"tag":104,"props":105,"children":107},"code",{"className":106},[],[108],{"type":49,"value":109},"code-testing-agent",{"type":49,"value":111},".",{"type":43,"tag":69,"props":113,"children":114},{},[115,117,123],{"type":49,"value":116},"The user wants a repository-wide testability audit. Use\n",{"type":43,"tag":104,"props":118,"children":120},{"className":119},[],[121],{"type":49,"value":122},"detect-static-dependencies",{"type":49,"value":111},{"type":43,"tag":69,"props":125,"children":126},{},[127,129,135],{"type":49,"value":128},"The user wants wrappers generated but not call sites\u002Ftests changed. Use\n",{"type":43,"tag":104,"props":130,"children":132},{"className":131},[],[133],{"type":49,"value":134},"generate-testability-wrappers",{"type":49,"value":111},{"type":43,"tag":69,"props":137,"children":138},{},[139,141,147],{"type":49,"value":140},"The user requests a broad mechanical migration. Use\n",{"type":43,"tag":104,"props":142,"children":144},{"className":143},[],[145],{"type":49,"value":146},"migrate-static-to-wrapper",{"type":49,"value":148},", then generate tests separately.",{"type":43,"tag":69,"props":150,"children":151},{},[152],{"type":49,"value":153},"The code is not C#\u002F.NET.",{"type":43,"tag":58,"props":155,"children":157},{"id":156},"inputs",[158],{"type":49,"value":159},"Inputs",{"type":43,"tag":161,"props":162,"children":163},"table",{},[164,188],{"type":43,"tag":165,"props":166,"children":167},"thead",{},[168],{"type":43,"tag":169,"props":170,"children":171},"tr",{},[172,178,183],{"type":43,"tag":173,"props":174,"children":175},"th",{},[176],{"type":49,"value":177},"Input",{"type":43,"tag":173,"props":179,"children":180},{},[181],{"type":49,"value":182},"Required",{"type":43,"tag":173,"props":184,"children":185},{},[186],{"type":49,"value":187},"Description",{"type":43,"tag":189,"props":190,"children":191},"tbody",{},[192,211,229],{"type":43,"tag":169,"props":193,"children":194},{},[195,201,206],{"type":43,"tag":196,"props":197,"children":198},"td",{},[199],{"type":49,"value":200},"Behavior to test",{"type":43,"tag":196,"props":202,"children":203},{},[204],{"type":49,"value":205},"Yes",{"type":43,"tag":196,"props":207,"children":208},{},[209],{"type":49,"value":210},"The method\u002Fworkflow and expected observable behavior",{"type":43,"tag":169,"props":212,"children":213},{},[214,219,224],{"type":43,"tag":196,"props":215,"children":216},{},[217],{"type":49,"value":218},"Target scope",{"type":43,"tag":196,"props":220,"children":221},{},[222],{"type":49,"value":223},"No",{"type":43,"tag":196,"props":225,"children":226},{},[227],{"type":49,"value":228},"Discover the narrowest relevant file\u002Fproject when omitted",{"type":43,"tag":169,"props":230,"children":231},{},[232,237,241],{"type":43,"tag":196,"props":233,"children":234},{},[235],{"type":49,"value":236},"Allowed production changes",{"type":43,"tag":196,"props":238,"children":239},{},[240],{"type":49,"value":223},{"type":43,"tag":196,"props":242,"children":243},{},[244],{"type":49,"value":245},"Default to the minimum internal\u002Fconstructor seam",{"type":43,"tag":58,"props":247,"children":249},{"id":248},"workflow",[250],{"type":49,"value":251},"Workflow",{"type":43,"tag":253,"props":254,"children":256},"h3",{"id":255},"step-1-prove-the-obstacle",[257],{"type":49,"value":258},"Step 1: Prove the obstacle",{"type":43,"tag":52,"props":260,"children":261},{},[262],{"type":49,"value":263},"Read the target production path and its existing tests. Identify the exact ambient\noperation preventing a deterministic test and the behavior that must remain\nunchanged. Do not run a repository-wide static scan for a single-class request.",{"type":43,"tag":52,"props":265,"children":266},{},[267],{"type":49,"value":268},"If an adequate seam already exists, stop refactoring and use it. This skill adds\nno value when a fake can already be supplied.",{"type":43,"tag":253,"props":270,"children":272},{"id":271},"step-2-select-the-smallest-safe-seam",[273],{"type":49,"value":274},"Step 2: Select the smallest safe seam",{"type":43,"tag":52,"props":276,"children":277},{},[278],{"type":49,"value":279},"Choose by dependency and repository constraints:",{"type":43,"tag":161,"props":281,"children":282},{},[283,299],{"type":43,"tag":165,"props":284,"children":285},{},[286],{"type":43,"tag":169,"props":287,"children":288},{},[289,294],{"type":43,"tag":173,"props":290,"children":291},{},[292],{"type":49,"value":293},"Dependency",{"type":43,"tag":173,"props":295,"children":296},{},[297],{"type":49,"value":298},"Preferred seam",{"type":43,"tag":189,"props":300,"children":301},{},[302,331,352,381,401],{"type":43,"tag":169,"props":303,"children":304},{},[305,310],{"type":43,"tag":196,"props":306,"children":307},{},[308],{"type":49,"value":309},"Current time \u002F timers",{"type":43,"tag":196,"props":311,"children":312},{},[313,315,321,323,329],{"type":49,"value":314},"Inject ",{"type":43,"tag":104,"props":316,"children":318},{"className":317},[],[319],{"type":49,"value":320},"TimeProvider",{"type":49,"value":322},"; use ",{"type":43,"tag":104,"props":324,"children":326},{"className":325},[],[327],{"type":49,"value":328},"FakeTimeProvider",{"type":49,"value":330}," in tests",{"type":43,"tag":169,"props":332,"children":333},{},[334,339],{"type":43,"tag":196,"props":335,"children":336},{},[337],{"type":49,"value":338},"Filesystem",{"type":43,"tag":196,"props":340,"children":341},{},[342,344,350],{"type":49,"value":343},"Existing repository file abstraction; otherwise the smallest interface or ",{"type":43,"tag":104,"props":345,"children":347},{"className":346},[],[348],{"type":49,"value":349},"System.IO.Abstractions",{"type":49,"value":351}," when already used\u002Faccepted",{"type":43,"tag":169,"props":353,"children":354},{},[355,360],{"type":43,"tag":196,"props":356,"children":357},{},[358],{"type":49,"value":359},"HTTP",{"type":43,"tag":196,"props":361,"children":362},{},[363,365,371,373,379],{"type":49,"value":364},"Existing typed ",{"type":43,"tag":104,"props":366,"children":368},{"className":367},[],[369],{"type":49,"value":370},"HttpClient",{"type":49,"value":372},"\u002Fhandler or ",{"type":43,"tag":104,"props":374,"children":376},{"className":375},[],[377],{"type":49,"value":378},"IHttpClientFactory",{"type":49,"value":380}," seam",{"type":43,"tag":169,"props":382,"children":383},{},[384,389],{"type":43,"tag":196,"props":385,"children":386},{},[387],{"type":49,"value":388},"Randomness",{"type":43,"tag":196,"props":390,"children":391},{},[392,393,399],{"type":49,"value":314},{"type":43,"tag":104,"props":394,"children":396},{"className":395},[],[397],{"type":49,"value":398},"Random",{"type":49,"value":400}," or a minimal generator interface",{"type":43,"tag":169,"props":402,"children":403},{},[404,409],{"type":43,"tag":196,"props":405,"children":406},{},[407],{"type":49,"value":408},"Environment\u002Fconsole\u002Fprocess",{"type":43,"tag":196,"props":410,"children":411},{},[412],{"type":49,"value":413},"Minimal interface containing only members used by the target",{"type":43,"tag":52,"props":415,"children":416},{},[417,419,425,427,433,435,441],{"type":49,"value":418},"The scoped ",{"type":43,"tag":104,"props":420,"children":422},{"className":421},[],[423],{"type":49,"value":424},"AsyncLocal\u003CT>",{"type":49,"value":426}," rule applies to every static API that must retain its\npublic static shape — clocks, filesystem access, environment lookups, identity\ngeneration, and randomness. The scope captures and restores the previous value;\nnever implement ",{"type":43,"tag":104,"props":428,"children":430},{"className":429},[],[431],{"type":49,"value":432},"Dispose()",{"type":49,"value":434}," as an unconditional assignment to ",{"type":43,"tag":104,"props":436,"children":438},{"className":437},[],[439],{"type":49,"value":440},"null",{"type":49,"value":111},{"type":43,"tag":52,"props":443,"children":444},{},[445],{"type":49,"value":446},"Constructor injection is the default for instance classes. Reuse the repository's\nDI and naming conventions, but do not add a DI container to a class library just\nto satisfy this workflow.",{"type":43,"tag":52,"props":448,"children":449},{},[450],{"type":49,"value":451},"For a static class or a public API that cannot change, use a scoped ambient seam\nonly when constructor\u002Fparameter injection is impossible. The override must:",{"type":43,"tag":65,"props":453,"children":454},{},[455,483,496,501],{"type":43,"tag":69,"props":456,"children":457},{},[458,460,466,468,473,475,481],{"type":49,"value":459},"flow across ",{"type":43,"tag":104,"props":461,"children":463},{"className":462},[],[464],{"type":49,"value":465},"await",{"type":49,"value":467}," (",{"type":43,"tag":104,"props":469,"children":471},{"className":470},[],[472],{"type":49,"value":424},{"type":49,"value":474},", not ",{"type":43,"tag":104,"props":476,"children":478},{"className":477},[],[479],{"type":49,"value":480},"[ThreadStatic]",{"type":49,"value":482},");",{"type":43,"tag":69,"props":484,"children":485},{},[486,488,494],{"type":49,"value":487},"return ",{"type":43,"tag":104,"props":489,"children":491},{"className":490},[],[492],{"type":49,"value":493},"IDisposable",{"type":49,"value":495}," and restore the previous value, including nested scopes;",{"type":43,"tag":69,"props":497,"children":498},{},[499],{"type":49,"value":500},"default to the real production dependency;",{"type":43,"tag":69,"props":502,"children":503},{},[504],{"type":49,"value":505},"avoid a process-global mutable fake that makes tests non-parallel.",{"type":43,"tag":52,"props":507,"children":508},{},[509,511,517],{"type":49,"value":510},"Use built-in fake-time-aware overloads instead of inventing an ",{"type":43,"tag":104,"props":512,"children":514},{"className":513},[],[515],{"type":49,"value":516},"IDelay",{"type":49,"value":518}," wrapper:",{"type":43,"tag":161,"props":520,"children":521},{},[522,538],{"type":43,"tag":165,"props":523,"children":524},{},[525],{"type":43,"tag":169,"props":526,"children":527},{},[528,533],{"type":43,"tag":173,"props":529,"children":530},{},[531],{"type":49,"value":532},"Ambient operation",{"type":43,"tag":173,"props":534,"children":535},{},[536],{"type":49,"value":537},"Replacement",{"type":43,"tag":189,"props":539,"children":540},{},[541,562,583],{"type":43,"tag":169,"props":542,"children":543},{},[544,553],{"type":43,"tag":196,"props":545,"children":546},{},[547],{"type":43,"tag":104,"props":548,"children":550},{"className":549},[],[551],{"type":49,"value":552},"Task.Delay(delay, token)",{"type":43,"tag":196,"props":554,"children":555},{},[556],{"type":43,"tag":104,"props":557,"children":559},{"className":558},[],[560],{"type":49,"value":561},"Task.Delay(delay, timeProvider, token)",{"type":43,"tag":169,"props":563,"children":564},{},[565,574],{"type":43,"tag":196,"props":566,"children":567},{},[568],{"type":43,"tag":104,"props":569,"children":571},{"className":570},[],[572],{"type":49,"value":573},"new CancellationTokenSource(delay)",{"type":43,"tag":196,"props":575,"children":576},{},[577],{"type":43,"tag":104,"props":578,"children":580},{"className":579},[],[581],{"type":49,"value":582},"new CancellationTokenSource(delay, timeProvider)",{"type":43,"tag":169,"props":584,"children":585},{},[586,595],{"type":43,"tag":196,"props":587,"children":588},{},[589],{"type":43,"tag":104,"props":590,"children":592},{"className":591},[],[593],{"type":49,"value":594},"PeriodicTimer(period)",{"type":43,"tag":196,"props":596,"children":597},{},[598,604],{"type":43,"tag":104,"props":599,"children":601},{"className":600},[],[602],{"type":49,"value":603},"new PeriodicTimer(period, timeProvider)",{"type":49,"value":605}," when the target framework provides it",{"type":43,"tag":52,"props":607,"children":608},{},[609,611,616],{"type":49,"value":610},"Test delayed behavior by starting the operation, proving it is incomplete,\nadvancing ",{"type":43,"tag":104,"props":612,"children":614},{"className":613},[],[615],{"type":49,"value":328},{"type":49,"value":617},", then awaiting it. Never wait for wall-clock time.",{"type":43,"tag":52,"props":619,"children":620},{},[621],{"type":49,"value":622},"For a nested ambient override, disposing the inner scope must restore the outer\nvalue, not clear the slot. Capture the previous value per scope:",{"type":43,"tag":624,"props":625,"children":629},"pre",{"className":626,"code":627,"language":14,"meta":628,"style":628},"language-csharp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","public static IDisposable OverrideClock(Func\u003CDateTimeOffset> clock)\n{\n    var previous = s_clock.Value;\n    s_clock.Value = clock;\n    return new Scope(() => s_clock.Value = previous);\n}\n","",[630],{"type":43,"tag":104,"props":631,"children":632},{"__ignoreMap":628},[633,644,653,662,671,680],{"type":43,"tag":634,"props":635,"children":638},"span",{"class":636,"line":637},"line",1,[639],{"type":43,"tag":634,"props":640,"children":641},{},[642],{"type":49,"value":643},"public static IDisposable OverrideClock(Func\u003CDateTimeOffset> clock)\n",{"type":43,"tag":634,"props":645,"children":647},{"class":636,"line":646},2,[648],{"type":43,"tag":634,"props":649,"children":650},{},[651],{"type":49,"value":652},"{\n",{"type":43,"tag":634,"props":654,"children":656},{"class":636,"line":655},3,[657],{"type":43,"tag":634,"props":658,"children":659},{},[660],{"type":49,"value":661},"    var previous = s_clock.Value;\n",{"type":43,"tag":634,"props":663,"children":665},{"class":636,"line":664},4,[666],{"type":43,"tag":634,"props":667,"children":668},{},[669],{"type":49,"value":670},"    s_clock.Value = clock;\n",{"type":43,"tag":634,"props":672,"children":674},{"class":636,"line":673},5,[675],{"type":43,"tag":634,"props":676,"children":677},{},[678],{"type":49,"value":679},"    return new Scope(() => s_clock.Value = previous);\n",{"type":43,"tag":634,"props":681,"children":683},{"class":636,"line":682},6,[684],{"type":43,"tag":634,"props":685,"children":686},{},[687],{"type":49,"value":688},"}\n",{"type":43,"tag":52,"props":690,"children":691},{},[692],{"type":49,"value":693},"Add tests for both nesting and parallel async flows; parallel-only tests do not\ncatch the common \"dispose sets null\" bug.",{"type":43,"tag":253,"props":695,"children":697},{"id":696},"step-3-preserve-behavior-and-api-shape",[698],{"type":49,"value":699},"Step 3: Preserve behavior and API shape",{"type":43,"tag":52,"props":701,"children":702},{},[703],{"type":49,"value":704},"Keep the production change mechanical:",{"type":43,"tag":65,"props":706,"children":707},{},[708,713,718,730,735],{"type":43,"tag":69,"props":709,"children":710},{},[711],{"type":49,"value":712},"Wrap only members used by the target behavior.",{"type":43,"tag":69,"props":714,"children":715},{},[716],{"type":49,"value":717},"Default implementations delegate directly to the original API.",{"type":43,"tag":69,"props":719,"children":720},{},[721,723,729],{"type":49,"value":722},"Preserve exceptions, path handling, time zone, and ",{"type":43,"tag":104,"props":724,"children":726},{"className":725},[],[727],{"type":49,"value":728},"DateTime.Kind",{"type":49,"value":111},{"type":43,"tag":69,"props":731,"children":732},{},[733],{"type":49,"value":734},"Keep existing public signatures unless the user explicitly permits an API change.",{"type":43,"tag":69,"props":736,"children":737},{},[738],{"type":49,"value":739},"Do not move business logic into the wrapper or fix unrelated production bugs.",{"type":43,"tag":52,"props":741,"children":742},{},[743],{"type":49,"value":744},"For time replacements:",{"type":43,"tag":65,"props":746,"children":747},{},[748,765,781,797],{"type":43,"tag":69,"props":749,"children":750},{},[751,757,759],{"type":43,"tag":104,"props":752,"children":754},{"className":753},[],[755],{"type":49,"value":756},"DateTime.UtcNow",{"type":49,"value":758}," -> ",{"type":43,"tag":104,"props":760,"children":762},{"className":761},[],[763],{"type":49,"value":764},"timeProvider.GetUtcNow().UtcDateTime",{"type":43,"tag":69,"props":766,"children":767},{},[768,774,775],{"type":43,"tag":104,"props":769,"children":771},{"className":770},[],[772],{"type":49,"value":773},"DateTime.Now",{"type":49,"value":758},{"type":43,"tag":104,"props":776,"children":778},{"className":777},[],[779],{"type":49,"value":780},"timeProvider.GetLocalNow().LocalDateTime",{"type":43,"tag":69,"props":782,"children":783},{},[784,790,791],{"type":43,"tag":104,"props":785,"children":787},{"className":786},[],[788],{"type":49,"value":789},"DateTimeOffset.UtcNow",{"type":49,"value":758},{"type":43,"tag":104,"props":792,"children":794},{"className":793},[],[795],{"type":49,"value":796},"timeProvider.GetUtcNow()",{"type":43,"tag":69,"props":798,"children":799},{},[800,806,807],{"type":43,"tag":104,"props":801,"children":803},{"className":802},[],[804],{"type":49,"value":805},"DateTimeOffset.Now",{"type":49,"value":758},{"type":43,"tag":104,"props":808,"children":810},{"className":809},[],[811],{"type":49,"value":812},"timeProvider.GetLocalNow()",{"type":43,"tag":253,"props":814,"children":816},{"id":815},"step-4-keep-production-defaults-wired",[817],{"type":49,"value":818},"Step 4: Keep production defaults wired",{"type":43,"tag":52,"props":820,"children":821},{},[822],{"type":49,"value":823},"Update every composition root or constructor call affected by the seam. Production\nmust still use real time\u002Ffilesystem\u002Fetc. by default. If the project uses DI,\nregister the default implementation with the lifetime matching repository\nconventions. If it does not use DI, compose explicitly; do not introduce a\ncontainer.",{"type":43,"tag":52,"props":825,"children":826},{},[827],{"type":49,"value":828},"Build the affected production project before writing tests. A compile failure here\nis a seam problem, not a test problem.",{"type":43,"tag":253,"props":830,"children":832},{"id":831},"step-5-write-deterministic-tests",[833],{"type":49,"value":834},"Step 5: Write deterministic tests",{"type":43,"tag":52,"props":836,"children":837},{},[838,840,846],{"type":49,"value":839},"Use the repository's existing test project. If none exists, invoke\n",{"type":43,"tag":104,"props":841,"children":843},{"className":842},[],[844],{"type":49,"value":845},"scaffold-dotnet-test-project",{"type":49,"value":847}," first.",{"type":43,"tag":52,"props":849,"children":850},{},[851],{"type":49,"value":852},"Tests must supply controlled dependencies:",{"type":43,"tag":65,"props":854,"children":855},{},[856,861,866],{"type":43,"tag":69,"props":857,"children":858},{},[859],{"type":49,"value":860},"fixed\u002Fadvanced time rather than wall-clock waiting;",{"type":43,"tag":69,"props":862,"children":863},{},[864],{"type":49,"value":865},"an in-memory fake filesystem or hand-rolled fake rather than temp\u002Freal files;",{"type":43,"tag":69,"props":867,"children":868},{},[869],{"type":49,"value":870},"no environment mutation, external process, console input, or network.",{"type":43,"tag":52,"props":872,"children":873},{},[874],{"type":49,"value":875},"Assert the requested business result and at least one interaction\u002Fstate observable\nthat proves the fake dependency drove the path. Include a production-default test\nonly when it can remain deterministic; never touch the real filesystem merely to\nprove the adapter delegates.",{"type":43,"tag":253,"props":877,"children":879},{"id":878},"step-6-verify-the-complete-path",[880],{"type":49,"value":881},"Step 6: Verify the complete path",{"type":43,"tag":52,"props":883,"children":884},{},[885],{"type":49,"value":886},"Run the affected production build, targeted test project, and repository-level\ntest command. Re-read the diff and confirm:",{"type":43,"tag":888,"props":889,"children":890},"ol",{},[891,896,901,906],{"type":43,"tag":69,"props":892,"children":893},{},[894],{"type":49,"value":895},"every production change is required by the seam;",{"type":43,"tag":69,"props":897,"children":898},{},[899],{"type":49,"value":900},"no real ambient resource is used by the new tests;",{"type":43,"tag":69,"props":902,"children":903},{},[904],{"type":49,"value":905},"current-time semantics and public behavior are preserved;",{"type":43,"tag":69,"props":907,"children":908},{},[909],{"type":49,"value":910},"existing tests were not replaced or duplicated.",{"type":43,"tag":58,"props":912,"children":914},{"id":913},"output-contract",[915],{"type":49,"value":916},"Output Contract",{"type":43,"tag":52,"props":918,"children":919},{},[920,922,928],{"type":49,"value":921},"Provide a compact ",{"type":43,"tag":104,"props":923,"children":925},{"className":924},[],[926],{"type":49,"value":927},"Requirement | Evidence",{"type":49,"value":929}," table. Cite the production seam,\nproduction default wiring, exact test names, and passing commands. If a package\nrestore or build blocks validation, report that blocker rather than claiming the\ntests pass.",{"type":43,"tag":58,"props":931,"children":933},{"id":932},"validation",[934],{"type":49,"value":935},"Validation",{"type":43,"tag":65,"props":937,"children":940},{"className":938},[939],"contains-task-list",[941,954,963,972,981,997,1006,1015],{"type":43,"tag":69,"props":942,"children":945},{"className":943},[944],"task-list-item",[946,952],{"type":43,"tag":947,"props":948,"children":951},"input",{"disabled":949,"type":950},true,"checkbox",[],{"type":49,"value":953}," The original obstacle was concrete and in the requested path.",{"type":43,"tag":69,"props":955,"children":957},{"className":956},[944],[958,961],{"type":43,"tag":947,"props":959,"children":960},{"disabled":949,"type":950},[],{"type":49,"value":962}," An existing seam was reused when available.",{"type":43,"tag":69,"props":964,"children":966},{"className":965},[944],[967,970],{"type":43,"tag":947,"props":968,"children":969},{"disabled":949,"type":950},[],{"type":49,"value":971}," The new abstraction exposes only members required by the target behavior.",{"type":43,"tag":69,"props":973,"children":975},{"className":974},[944],[976,979],{"type":43,"tag":947,"props":977,"children":978},{"disabled":949,"type":950},[],{"type":49,"value":980}," Production defaults still delegate to the original dependency.",{"type":43,"tag":69,"props":982,"children":984},{"className":983},[944],[985,988,990,995],{"type":43,"tag":947,"props":986,"children":987},{"disabled":949,"type":950},[],{"type":49,"value":989}," Time conversions preserve local\u002FUTC and ",{"type":43,"tag":104,"props":991,"children":993},{"className":992},[],[994],{"type":49,"value":728},{"type":49,"value":996}," semantics.",{"type":43,"tag":69,"props":998,"children":1000},{"className":999},[944],[1001,1004],{"type":43,"tag":947,"props":1002,"children":1003},{"disabled":949,"type":950},[],{"type":49,"value":1005}," Static ambient overrides are async-safe, scoped, nested, and reversible.",{"type":43,"tag":69,"props":1007,"children":1009},{"className":1008},[944],[1010,1013],{"type":43,"tag":947,"props":1011,"children":1012},{"disabled":949,"type":950},[],{"type":49,"value":1014}," New tests use fixed\u002Fin-memory dependencies and no real I\u002FO or wall clock.",{"type":43,"tag":69,"props":1016,"children":1018},{"className":1017},[944],[1019,1022],{"type":43,"tag":947,"props":1020,"children":1021},{"disabled":949,"type":950},[],{"type":49,"value":1023}," Production build and targeted\u002Frepository tests pass.",{"type":43,"tag":58,"props":1025,"children":1027},{"id":1026},"common-pitfalls",[1028],{"type":49,"value":1029},"Common Pitfalls",{"type":43,"tag":161,"props":1031,"children":1032},{},[1033,1049],{"type":43,"tag":165,"props":1034,"children":1035},{},[1036],{"type":43,"tag":169,"props":1037,"children":1038},{},[1039,1044],{"type":43,"tag":173,"props":1040,"children":1041},{},[1042],{"type":49,"value":1043},"Pitfall",{"type":43,"tag":173,"props":1045,"children":1046},{},[1047],{"type":49,"value":1048},"Corrective action",{"type":43,"tag":189,"props":1050,"children":1051},{},[1052,1065,1078,1119,1139,1152,1165],{"type":43,"tag":169,"props":1053,"children":1054},{},[1055,1060],{"type":43,"tag":196,"props":1056,"children":1057},{},[1058],{"type":49,"value":1059},"Refactoring before proving a blocker",{"type":43,"tag":196,"props":1061,"children":1062},{},[1063],{"type":49,"value":1064},"Reuse an existing seam and write the test directly",{"type":43,"tag":169,"props":1066,"children":1067},{},[1068,1073],{"type":43,"tag":196,"props":1069,"children":1070},{},[1071],{"type":49,"value":1072},"Wrapping an entire static API",{"type":43,"tag":196,"props":1074,"children":1075},{},[1076],{"type":49,"value":1077},"Expose only members exercised by the target",{"type":43,"tag":169,"props":1079,"children":1080},{},[1081,1100],{"type":43,"tag":196,"props":1082,"children":1083},{},[1084,1086,1092,1094],{"type":49,"value":1085},"Converting ",{"type":43,"tag":104,"props":1087,"children":1089},{"className":1088},[],[1090],{"type":49,"value":1091},"UtcNow",{"type":49,"value":1093}," with ",{"type":43,"tag":104,"props":1095,"children":1097},{"className":1096},[],[1098],{"type":49,"value":1099},".DateTime",{"type":43,"tag":196,"props":1101,"children":1102},{},[1103,1105,1111,1113],{"type":49,"value":1104},"Use ",{"type":43,"tag":104,"props":1106,"children":1108},{"className":1107},[],[1109],{"type":49,"value":1110},".UtcDateTime",{"type":49,"value":1112}," to preserve ",{"type":43,"tag":104,"props":1114,"children":1116},{"className":1115},[],[1117],{"type":49,"value":1118},"DateTimeKind.Utc",{"type":43,"tag":169,"props":1120,"children":1121},{},[1122,1127],{"type":43,"tag":196,"props":1123,"children":1124},{},[1125],{"type":49,"value":1126},"Mutable static fake shared by tests",{"type":43,"tag":196,"props":1128,"children":1129},{},[1130,1132,1137],{"type":49,"value":1131},"Use constructor injection or a scoped ",{"type":43,"tag":104,"props":1133,"children":1135},{"className":1134},[],[1136],{"type":49,"value":424},{"type":49,"value":1138}," override",{"type":43,"tag":169,"props":1140,"children":1141},{},[1142,1147],{"type":43,"tag":196,"props":1143,"children":1144},{},[1145],{"type":49,"value":1146},"Adding DI to a library with no container",{"type":43,"tag":196,"props":1148,"children":1149},{},[1150],{"type":49,"value":1151},"Compose the dependency explicitly",{"type":43,"tag":169,"props":1153,"children":1154},{},[1155,1160],{"type":43,"tag":196,"props":1156,"children":1157},{},[1158],{"type":49,"value":1159},"Using temp files as a shortcut",{"type":43,"tag":196,"props":1161,"children":1162},{},[1163],{"type":49,"value":1164},"Supply an in-memory fake; the scenario requires no real I\u002FO",{"type":43,"tag":169,"props":1166,"children":1167},{},[1168,1173],{"type":43,"tag":196,"props":1169,"children":1170},{},[1171],{"type":49,"value":1172},"Stopping after the refactor builds",{"type":43,"tag":196,"props":1174,"children":1175},{},[1176],{"type":49,"value":1177},"Write and run the behavior tests that justified the seam",{"type":43,"tag":1179,"props":1180,"children":1181},"style",{},[1182],{"type":49,"value":1183},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":1185,"total":1288},[1186,1203,1218,1236,1248,1266,1276],{"slug":1187,"name":1187,"fn":1188,"description":1189,"org":1190,"tags":1191,"stars":25,"repoUrl":26,"updatedAt":1202},"analyzing-dotnet-performance","analyze .NET code for performance anti-patterns","Scans .NET code for ~50 performance anti-patterns across async, memory, strings, collections, LINQ, regex, serialization, and I\u002FO with tiered severity classification. Use when analyzing .NET code for optimization opportunities, reviewing hot paths, or auditing allocation-heavy patterns.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1192,1195,1198,1199],{"name":1193,"slug":1194,"type":15},".NET","net",{"name":1196,"slug":1197,"type":15},"Code Analysis","code-analysis",{"name":23,"slug":24,"type":15},{"name":1200,"slug":1201,"type":15},"Performance","performance","2026-07-12T08:23:25.400375",{"slug":1204,"name":1204,"fn":1205,"description":1206,"org":1207,"tags":1208,"stars":25,"repoUrl":26,"updatedAt":1217},"android-tombstone-symbolication","symbolicate .NET runtime frames in Android tombstones","Symbolicate the .NET runtime frames in an Android tombstone file. Extracts BuildIds and PC offsets from the native backtrace, downloads debug symbols from the Microsoft symbol server, and runs llvm-symbolizer to produce function names with source file and line numbers. USE FOR triaging a .NET MAUI or Mono Android app crash from a tombstone, resolving native backtrace frames in libmonosgen-2.0.so or libcoreclr.so to .NET runtime source code, or investigating SIGABRT, SIGSEGV, or other native signals originating from the .NET runtime on Android. DO NOT USE FOR pure Java\u002FKotlin crashes, managed .NET exceptions that are already captured in logcat, or iOS crash logs. INVOKES Symbolicate-Tombstone.ps1 script, llvm-symbolizer, Microsoft symbol server.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1209,1210,1213,1214],{"name":1193,"slug":1194,"type":15},{"name":1211,"slug":1212,"type":15},"Android","android",{"name":23,"slug":24,"type":15},{"name":1215,"slug":1216,"type":15},"Microsoft","microsoft","2026-07-12T08:23:21.595572",{"slug":1219,"name":1219,"fn":1220,"description":1221,"org":1222,"tags":1223,"stars":25,"repoUrl":26,"updatedAt":1235},"apple-crash-symbolication","symbolicate .NET runtime frames in crash logs","Symbolicate .NET runtime frames in Apple platform .ips crash logs (iOS, tvOS, Mac Catalyst, macOS). Extracts UUIDs and addresses from the native backtrace, locates dSYM debug symbols, and runs atos to produce function names with source file and line numbers. Automatically downloads .dwarf symbols from the Microsoft symbol server using Mach-O UUIDs. USE FOR triaging a .NET MAUI or Mono app crash from an .ips file on any Apple platform, resolving native backtrace frames in libcoreclr or libmonosgen-2.0 to .NET runtime source code, retrieving .ips crash logs from a connected iOS device or iPhone, or investigating EXC_CRASH, EXC_BAD_ACCESS, SIGABRT, or SIGSEGV originating from the .NET runtime. DO NOT USE FOR pure Swift\u002FObjective-C crashes with no .NET components, or Android tombstone files. INVOKES Symbolicate-Crash.ps1 script, atos, dwarfdump, idevicecrashreport.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1224,1225,1226,1229,1232],{"name":1193,"slug":1194,"type":15},{"name":23,"slug":24,"type":15},{"name":1227,"slug":1228,"type":15},"iOS","ios",{"name":1230,"slug":1231,"type":15},"macOS","macos",{"name":1233,"slug":1234,"type":15},"Observability","observability","2026-07-12T08:23:20.369986",{"slug":1237,"name":1237,"fn":1238,"description":1239,"org":1240,"tags":1241,"stars":25,"repoUrl":26,"updatedAt":1247},"assertion-quality","evaluate assertion quality in test suites","MANDATORY for reviewing assertion strength, depth, and variety in existing tests. Invoke when the user asks whether individual assertions are weak, shallow, trivial, always true, self-referential, or diverse; asks which tests are assertion-free or rely only on presence\u002Ftruthiness checks; or requests assertion quality\u002Fdepth\u002Fvariety metrics. Polyglot: .NET, Python\u002Fpytest, TS\u002FJS\u002FJest, Java, Go, Ruby, Rust, Swift, Kotlin, PowerShell, C++. DO NOT USE FOR: writing or fixing tests\u002Fassertions (use code-testing-agent or writing-mstest-tests), mutation reasoning (use test-gap-analysis), or a general severity-ranked anti-pattern audit (use test-anti-patterns).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1242,1243,1246],{"name":1196,"slug":1197,"type":15},{"name":1244,"slug":1245,"type":15},"QA","qa",{"name":20,"slug":21,"type":15},"2026-08-07T04:38:19.067839",{"slug":1249,"name":1249,"fn":1250,"description":1251,"org":1252,"tags":1253,"stars":25,"repoUrl":26,"updatedAt":1265},"author-component","create and review Blazor components","Create or review Blazor components (.razor files) with correct architecture. USE FOR: writing new Blazor components that do NOT involve JavaScript interop, implementing parameters and EventCallback, RenderFragment slots, component lifecycle (OnInitializedAsync, OnParametersSet), async patterns, IAsyncDisposable, CancellationToken, CSS isolation, code-behind. DO NOT USE FOR: creating new projects (use create-blazor-project), JavaScript interop or calling browser APIs from Blazor (use use-js-interop), forms and validation (use collect-user-input), prerendering issues (use support-prerendering), HTTP data fetching patterns (use fetch-and-send-data), coordinating state between unrelated components (use coordinate-components).\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1254,1255,1258,1259,1262],{"name":1193,"slug":1194,"type":15},{"name":1256,"slug":1257,"type":15},"Blazor","blazor",{"name":13,"slug":14,"type":15},{"name":1260,"slug":1261,"type":15},"UI Components","ui-components",{"name":1263,"slug":1264,"type":15},"Web Development","web-development","2026-07-15T06:03:29.216359",{"slug":1267,"name":1267,"fn":1268,"description":1269,"org":1270,"tags":1271,"stars":25,"repoUrl":26,"updatedAt":1275},"binlog-failure-analysis","analyze MSBuild binary logs","Analyze MSBuild binary logs to diagnose build failures. USE FOR: build errors that are unclear from console output, diagnosing cascading failures across multi-project builds, tracing MSBuild target execution order, and generally any MSBuild build issues. Requires an existing .binlog file. DO NOT USE FOR: generating binlogs (use binlog-generation), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1272,1273,1274],{"name":1196,"slug":1197,"type":15},{"name":23,"slug":24,"type":15},{"name":1215,"slug":1216,"type":15},"2026-08-07T04:38:20.048408",{"slug":1277,"name":1277,"fn":1278,"description":1279,"org":1280,"tags":1281,"stars":25,"repoUrl":26,"updatedAt":1287},"binlog-generation","generate MSBuild binary logs for diagnostics","Generate MSBuild binary logs (binlogs) for build diagnostics and analysis. USE FOR: adding \u002Fbl:{} to any dotnet build, test, pack, publish, or restore command to capture a full build execution trace, prerequisite for binlog-failure-analysis and build-perf-diagnostics skills, enabling post-build investigation of errors or performance. Requires MSBuild 17.8+ \u002F .NET 8 SDK+ for {} placeholder; PowerShell needs -bl:{{}}. DO NOT USE FOR: non-MSBuild build systems (npm, Maven, CMake), analyzing an existing binlog (use binlog-failure-analysis instead).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1282,1285,1286],{"name":1283,"slug":1284,"type":15},"Build","build",{"name":23,"slug":24,"type":15},{"name":17,"slug":18,"type":15},"2026-07-19T05:38:19.340791",98,{"items":1290,"total":1395},[1291,1303,1310,1317,1325,1331,1339,1345,1351,1361,1374,1385],{"slug":1292,"name":1292,"fn":1293,"description":1294,"org":1295,"tags":1296,"stars":1300,"repoUrl":1301,"updatedAt":1302},"multithreaded-task-migration","migrate MSBuild tasks to multithreaded mode","Guide for migrating MSBuild tasks to multithreaded mode support, including compatibility red-team review. Use this when converting tasks to thread-safe versions, implementing IMultiThreadableTask, adding TaskEnvironment support, or auditing migrations for behavioral compatibility.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1297,1298,1299],{"name":1193,"slug":1194,"type":15},{"name":17,"slug":18,"type":15},{"name":1200,"slug":1201,"type":15},5535,"https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fmsbuild","2026-07-22T05:37:33.965588",{"slug":1187,"name":1187,"fn":1188,"description":1189,"org":1304,"tags":1305,"stars":25,"repoUrl":26,"updatedAt":1202},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1306,1307,1308,1309],{"name":1193,"slug":1194,"type":15},{"name":1196,"slug":1197,"type":15},{"name":23,"slug":24,"type":15},{"name":1200,"slug":1201,"type":15},{"slug":1204,"name":1204,"fn":1205,"description":1206,"org":1311,"tags":1312,"stars":25,"repoUrl":26,"updatedAt":1217},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1313,1314,1315,1316],{"name":1193,"slug":1194,"type":15},{"name":1211,"slug":1212,"type":15},{"name":23,"slug":24,"type":15},{"name":1215,"slug":1216,"type":15},{"slug":1219,"name":1219,"fn":1220,"description":1221,"org":1318,"tags":1319,"stars":25,"repoUrl":26,"updatedAt":1235},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1320,1321,1322,1323,1324],{"name":1193,"slug":1194,"type":15},{"name":23,"slug":24,"type":15},{"name":1227,"slug":1228,"type":15},{"name":1230,"slug":1231,"type":15},{"name":1233,"slug":1234,"type":15},{"slug":1237,"name":1237,"fn":1238,"description":1239,"org":1326,"tags":1327,"stars":25,"repoUrl":26,"updatedAt":1247},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1328,1329,1330],{"name":1196,"slug":1197,"type":15},{"name":1244,"slug":1245,"type":15},{"name":20,"slug":21,"type":15},{"slug":1249,"name":1249,"fn":1250,"description":1251,"org":1332,"tags":1333,"stars":25,"repoUrl":26,"updatedAt":1265},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1334,1335,1336,1337,1338],{"name":1193,"slug":1194,"type":15},{"name":1256,"slug":1257,"type":15},{"name":13,"slug":14,"type":15},{"name":1260,"slug":1261,"type":15},{"name":1263,"slug":1264,"type":15},{"slug":1267,"name":1267,"fn":1268,"description":1269,"org":1340,"tags":1341,"stars":25,"repoUrl":26,"updatedAt":1275},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1342,1343,1344],{"name":1196,"slug":1197,"type":15},{"name":23,"slug":24,"type":15},{"name":1215,"slug":1216,"type":15},{"slug":1277,"name":1277,"fn":1278,"description":1279,"org":1346,"tags":1347,"stars":25,"repoUrl":26,"updatedAt":1287},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1348,1349,1350],{"name":1283,"slug":1284,"type":15},{"name":23,"slug":24,"type":15},{"name":17,"slug":18,"type":15},{"slug":1352,"name":1352,"fn":1353,"description":1354,"org":1355,"tags":1356,"stars":25,"repoUrl":26,"updatedAt":1360},"build-parallelism","optimize MSBuild build parallelism","Diagnose and fix under-parallelized MSBuild builds. USE WHEN a multi-project solution build is slower than expected, doesn't speed up when you add cores, pegs a single core while others idle, or you want to know why `-m` isn't helping. Note: `\u002Fmaxcpucount` default is 1 (sequential) — always pass `-m` for parallel builds. Covers finding the critical path (longest serial ProjectReference chain), graph build (`\u002Fgraph`), BuildInParallel, and solution filters (`.slnf`). DO NOT USE FOR: single-project builds, incremental issues (use incremental-build), compilation slowness inside one project (use build-perf-diagnostics), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1357,1358,1359],{"name":1193,"slug":1194,"type":15},{"name":17,"slug":18,"type":15},{"name":1200,"slug":1201,"type":15},"2026-07-19T05:38:18.364937",{"slug":1362,"name":1362,"fn":1363,"description":1364,"org":1365,"tags":1366,"stars":25,"repoUrl":26,"updatedAt":1373},"build-perf-baseline","establish and optimize build performance baselines","Establish build performance baselines and apply systematic optimization techniques. USE FOR: diagnosing slow builds, establishing before\u002Fafter measurements (cold, warm, no-op scenarios), applying optimization strategies like MSBuild Server, static graph builds, artifacts output, and dependency graph trimming. Start here before diving into build-perf-diagnostics, incremental-build, or build-parallelism. DO NOT USE FOR: non-MSBuild build systems, detailed bottleneck analysis (use build-perf-diagnostics after baselining).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1367,1368,1371,1372],{"name":17,"slug":18,"type":15},{"name":1369,"slug":1370,"type":15},"Monitoring","monitoring",{"name":1200,"slug":1201,"type":15},{"name":20,"slug":21,"type":15},"2026-07-12T08:21:35.865649",{"slug":1375,"name":1375,"fn":1376,"description":1377,"org":1378,"tags":1379,"stars":25,"repoUrl":26,"updatedAt":1384},"build-perf-diagnostics","diagnose MSBuild build performance bottlenecks","Diagnose MSBuild build performance bottlenecks using binary log analysis. USE FOR: identifying why builds are slow by analyzing binlog performance summaries, detecting ResolveAssemblyReference (RAR) taking >5s, Roslyn analyzers consuming >30% of Csc time, single targets dominating >50% of build time, node utilization below 80%, excessive Copy tasks, NuGet restore running every build. Covers timeline analysis, Target\u002FTask Performance Summary interpretation, and 7 common bottleneck categories. Use after build-perf-baseline has established measurements. DO NOT USE FOR: establishing initial baselines (use build-perf-baseline first), fixing incremental build issues (use incremental-build), parallelism tuning (use build-parallelism), non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1380,1381,1382,1383],{"name":1193,"slug":1194,"type":15},{"name":23,"slug":24,"type":15},{"name":17,"slug":18,"type":15},{"name":1200,"slug":1201,"type":15},"2026-07-12T08:21:40.961722",{"slug":1386,"name":1386,"fn":1387,"description":1388,"org":1389,"tags":1390,"stars":25,"repoUrl":26,"updatedAt":1394},"check-bin-obj-clash","detect MSBuild output path conflicts","Detects MSBuild projects with conflicting OutputPath or IntermediateOutputPath. USE FOR: builds failing with 'Cannot create a file when that file already exists', 'The process cannot access the file because it is being used by another process', intermittent build failures that succeed on retry, or missing\u002Foverwritten outputs in multi-project or multi-targeting builds where bin\u002Fobj (or project.assets.json) collide. Common causes: shared OutputPath, missing AppendTargetFrameworkToOutputPath, extra global properties (e.g. PublishReadyToRun), or SetTargetFramework on a ProjectReference to a single-targeting project. DO NOT USE FOR: file access errors unrelated to MSBuild (OS-level locking), single-project single-TFM builds, non-MSBuild build systems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1391,1392,1393],{"name":23,"slug":24,"type":15},{"name":17,"slug":18,"type":15},{"name":1244,"slug":1245,"type":15},"2026-07-19T05:38:14.336279",146]