[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M699DQJQ7VE56EP4E91W2X58":3,"skill-same:01M699DQJQ7VE56EP4E91W2X58":96},{"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":26,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":55,"commitSha":56,"skillDirectory":57,"forksCount":58,"repositoryHtmlUrl":59,"repositoryHomepageUrl":6,"ownerHtmlUrl":60,"ownerBlogUrl":61,"ownerDescription":62,"updatedAt":63,"install":64,"resources":77},"01M699DQJQ7VE56EP4E91W2X58","detect-static-dependencies",null,"ACTIVATION PREREQUISITE: the request or discovered target must explicitly identify C#, .NET, `.cs`, or `.csproj`; otherwise stay dormant without invoking this skill. USE FOR: locating System.DateTime.Now\u002FUtcNow, System.IO.File\u002FDirectory, System.Environment, HttpClient, Console, or Process usage in C#; auditing C# code for hard-to-test framework dependencies; or verifying those C# calls are already abstracted. DO NOT USE FOR: any target lacking the activation prerequisite; generating wrappers (use generate-testability-wrappers); migrating code (use migrate-static-to-wrapper); or general code review.","microsoft\u002Ftestfx","microsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6154722?v=4","01M698VYDXZ6JW5WH3CRV650R8","Microsoft","01M699C71CJ91B5WZ3AXG3GG2M",1059,"MIT","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fdetect-static-dependencies\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000007","代码审查","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"static","detect static dependencies","detect","dependencies","activation","request","csproj","usage","datetime","now","io","do","code review","Wrapper","migrate","framework","code","generate","test framework","test","review","directory","2026-11-10 10:12:30",false,"---\nname: detect-static-dependencies\ndescription: >\n  ACTIVATION PREREQUISITE: the request or discovered target must explicitly\n  identify C#, .NET, `.cs`, or `.csproj`; otherwise stay dormant without\n  invoking this skill. USE FOR: locating\n  System.DateTime.Now\u002FUtcNow, System.IO.File\u002FDirectory, System.Environment,\n  HttpClient, Console, or Process usage in C#; auditing C# code for hard-to-test\n  framework dependencies; or verifying those C# calls are already abstracted.\n  DO NOT USE FOR: any target lacking the activation prerequisite; generating\n  wrappers (use generate-testability-wrappers); migrating code (use\n  migrate-static-to-wrapper); or general code review.\nlicense: MIT\n---\n\n# Detect Static Dependencies\n\nScan a C# codebase for calls to hard-to-test static APIs and produce a ranked report showing which statics appear most frequently, which files are most affected, and which abstractions already exist in the .NET ecosystem to replace them.\n\n## When to Use\n\n- Auditing a project's testability before adding unit tests\n- Understanding the scope of static coupling in a legacy codebase\n- Prioritizing which statics to wrap first (highest-frequency wins)\n- Creating a migration plan for incremental testability improvements\n\n## Response Guidelines\n\n- Scale the response to the user's request. A question about a specific category (e.g., \"find time statics\") should focus on that category with file locations and counts, not produce a full report across all categories.\n- When the user provides a specific file or directory path, scan only that scope — do not expand to the entire solution unless asked.\n- The full structured report format in Step 4 is for comprehensive audit requests. For focused questions, return only the relevant subset (e.g., category summary + affected files for the requested category).\n\n## Execution Contract\n\n- A relative path named in the prompt is enough to start. Discover it with the\n  available file-listing tools and scan it immediately; do not ask the user to\n  provide or re-upload files before both discovery and a content search fail.\n- Start with a recursive, line-numbered content search over eligible `.cs`\n  files. Do not search only for the `static` keyword: ambient calls inside\n  LINQ expressions, lambdas, callbacks, and interpolated strings usually have\n  no `static` modifier.\n- If a file-reading tool fails on a path that listing or search proved exists,\n  classify the failure before retrying. Fall back to another available\n  mechanism such as `rg -n`, grep, or a shell file reader only for confirmed\n  tool availability, transport, or path-normalization failures and only after\n  verifying the canonical path remains inside the workspace. Stop on\n  content-exclusion, permission\u002Fpolicy, workspace-boundary, or unknown failures.\n  Search output can seed the occurrence ledger; open only the surrounding code\n  needed to verify receiver provenance.\n- Never stop after loading this skill or announcing a scan plan. Return the\n  completed audit in the same response. If every fallback genuinely fails,\n  report the verified partial findings and the exact limitation; do not invent\n  findings or replace the audit with a request to rerun.\n\n## When Not to Use\n\n- The user wants wrappers generated (hand off to `generate-testability-wrappers`)\n- The user wants mechanical migration done (hand off to `migrate-static-to-wrapper`)\n- The statics are already behind interfaces or `TimeProvider`\n- The code is not C# \u002F .NET\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Target path | No | A file, directory, project (.csproj), or solution (.sln) to scan. Defaults to the current workspace. |\n| Exclusion patterns | No | Glob patterns to skip (e.g., `**\u002Fobj\u002F**`, `**\u002FMigrations\u002F**`) |\n| Category filter | No | Limit to specific categories: `time`, `filesystem`, `environment`, `network`, `console`, `process` |\n\n## Workflow\n\n### Step 1: Determine scan scope\n\nResolve the target to a set of `.cs` files:\n- Treat a prompt-named workspace-relative path as the target; locate it rather\n  than asking the user for an absolute path.\n- If omitted, scan every eligible `.cs` file under the current workspace; do not\n  pick one project and silently omit its siblings.\n- If a `.cs` file, scan that single file.\n- If a directory, scan all `.cs` files recursively (excluding `obj\u002F`, `bin\u002F`).\n- If a `.csproj`, find its directory and scan `.cs` files within.\n- If a `.sln`, parse it, find all project directories, and scan `.cs` files across all projects.\n\nAlways exclude `obj\u002F`, `bin\u002F`, and any user-specified exclusion patterns.\n\n### Step 2: Search for static dependency patterns\n\nScan each file for calls matching these categories:\n\nTreat pattern matches as candidates, not findings. Before counting an instance call, trace how its\nreceiver enters the class. A collaborator supplied through a constructor, parameter, property, or\ndependency injection (DI) is already a test seam. In particular, an injected `HttpClient` is\ntestable with a controlled `HttpMessageHandler`; do not count its calls or recommend replacing it\nmerely because the injected type is concrete.\n\n| Category | Patterns to search for | Recommended replacement |\n|----------|----------------------|------------------------|\n| **Time** | `DateTime.Now`, `DateTime.UtcNow`, `DateTime.Today`, `DateTimeOffset.Now`, `DateTimeOffset.UtcNow`, `Task.Delay(`, `new CancellationTokenSource(TimeSpan` | `TimeProvider` (.NET 8+) |\n| **File System** | `File.ReadAllText(`, `File.WriteAllText(`, `File.Exists(`, `File.Delete(`, `File.Copy(`, `File.Move(`, `Directory.Exists(`, `Directory.CreateDirectory(`, `Directory.GetFiles(`, `Directory.Delete(`, `Path.GetTempPath(`, and instance members that hit the disk (`new FileInfo(...)`, `new DirectoryInfo(...)`, `.LastWriteTimeUtc`, `new StreamReader(path)`) | `IFileSystem` (System.IO.Abstractions NuGet) |\n| **Randomness \u002F identity** | `new Random(`, `Random.Shared`, `Guid.NewGuid(` | `TimeProvider`-style seam: inject `Random` \u002F an `IGuidProvider` |\n| **Culture \u002F serialization** | `CultureInfo.CurrentCulture`, `CultureInfo.CurrentUICulture`, `JsonSerializer.Serialize(`, `JsonSerializer.Deserialize(` | Pass culture\u002Foptions explicitly, or inject a serializer abstraction |\n| **Environment** | `Environment.GetEnvironmentVariable(`, `Environment.SetEnvironmentVariable(`, `Environment.MachineName`, `Environment.UserName`, `Environment.CurrentDirectory`, `Environment.Exit(` | Custom `IEnvironmentProvider` |\n| **Network** | `new HttpClient(`, `.GetAsync(`, `.PostAsync(`, `.SendAsync(` (confirm the receiver is an `HttpClient`; exclude calls whose receiver is injected or produced by an injected factory) | Inject `HttpClient` (commonly supplied by `IHttpClientFactory`) |\n| **Console** | `Console.WriteLine(`, `Console.ReadLine(`, `Console.Write(`, `Console.ReadKey(` | `IConsole` wrapper or `ILogger` |\n| **Process** | `Process.Start(`, `Process.GetCurrentProcess(`, `Process.GetProcessesByName(` | Custom `IProcessRunner` |\n\nFor time calls, inspect use as well as count. Two ambient clock reads in one\nlogical operation are two call sites and a consistency defect: for example,\nseparate `DateTime.UtcNow` reads for `CreatedAt` and\n`ExpiresAt = DateTime.UtcNow.AddDays(30)` can drift. Recommend one captured\ninstant. With `TimeProvider`, retain `DateTimeOffset` where possible; when the\nexisting member requires UTC `DateTime`, use `GetUtcNow().UtcDateTime`, never\n`.DateTime`, which loses the UTC kind. Treat capturing one instant as an\noptional behavior-level follow-up: a mechanical wrapper migration must preserve\nthe original reads one-for-one unless the user separately approves that\nsemantic change.\n\n### Step 3: Aggregate and rank results\n\nCount each call site across the entire scan scope — including the instance-member call sites covered by the rules below, not only `static` ones.\n\n**Counting rules — inaccurate totals are the main way this report loses to an ad-hoc scan:**\n\n- **Build one occurrence ledger before writing prose.** Give each included call\n  site exactly one row containing category, exact pattern, `file:line`, and\n  recommended seam. Derive every category, pattern, and per-file count by\n  grouping that same ledger; never recount independently while writing tables.\n- **Keep the three count domains separate.** `Files scanned` includes every\n  eligible source file; `affected files` includes only files with ledger rows;\n  `call sites` is the number of ledger rows. Never substitute one for another.\n- **One authoritative total.** Every call site you found belongs in the category summary and the grand total. Never park real findings in an \"additional observations\" section that the totals exclude.\n- **Classify by what the member touches, not by whether it is `static`.** Instance members that reach the same untestable resource still count and belong in the matching category (`new FileInfo(path).LastWriteTimeUtc` → File System; `new HttpClient().GetAsync(...)` → Network). Say \"hidden dependency\", not \"static\", when the member is an instance call.\n- **Check receiver provenance before counting instance calls.** Count a resource access only when the code under test acquires or constructs the dependency itself. Exclude constructor-, parameter-, property-, and DI-injected collaborators from the \"needs wrapping\" total, including concrete `HttpClient` instances.\n- **Exclude deterministic pure helpers from the \"needs wrapping\" total.** `Path.Combine`, `Path.GetExtension`, `Path.GetFileName`, and `Math.*`\u002F`string.*` statics take no ambient input and are trivially testable. List them, if at all, in a separate \"no action needed\" note — never as testability blockers.\n- **Cover every category before reporting** — time, file system, environment, network, console, process, randomness (`new Random()`, `Guid.NewGuid()`), culture (`CultureInfo.CurrentCulture`), and serialization\u002Fstatics such as `JsonSerializer`. Omitting a category that is present is an under-count.\n- **Give `file:line` for every occurrence** so the user can jump straight to it.\n- **Reconcile before publishing.** The category totals, the top-patterns table, and the per-file table must sum to the same grand total.\n- **Treat exclusions as a scope decision, not a category.** Remove `obj\u002F`,\n  `bin\u002F`, generated, and user-excluded files before building the ledger. Do not\n  include their files or call sites in any reported count. State the exclusions\n  once rather than mixing excluded candidates into the arithmetic.\n- **Label truncated rankings.** In a comprehensive audit, list all distinct\n  patterns when needed for reconciliation. If the user asked only for a top-N\n  subset, label it as a subset and do not imply that its rows sum to the grand\n  total.\n\nProduce a summary with:\n\n1. **Category summary** — total call sites per category (time, filesystem, env, etc.)\n2. **Top patterns** — the 10 most frequent individual patterns ranked by count\n3. **Most affected files** — files with the highest number of static dependencies\n4. **Existing abstractions available** — for each category, note the recommended .NET abstraction:\n   - Time → `TimeProvider` (built-in since .NET 8)\n   - File system → `System.IO.Abstractions` (NuGet package)\n   - HTTP → `IHttpClientFactory` (built-in)\n   - Environment → custom `IEnvironmentProvider`\n   - Console → custom `IConsole` or `ILogger`\n   - Process → custom `IProcessRunner`\n\n### Step 4: Present the report\n\nFormat the output as a structured report:\n\n```\n## Static Dependency Report\n\n**Scope**: \u003Cproject\u002Fsolution name>\n**Files scanned**: \u003Ccount>\n**Total static call sites**: \u003Ccount>\n\n### Category Summary\n| Category     | Call Sites | Recommended Abstraction |\n|-------------|-----------|------------------------|\n| Time         | 42        | TimeProvider (.NET 8+) |\n| File System  | 31        | System.IO.Abstractions |\n| Environment  | 12        | IEnvironmentProvider   |\n| ...          | ...       | ...                    |\n\n### Top 10 Patterns\n| # | Pattern             | Count | Files |\n|---|---------------------|-------|-------|\n| 1 | DateTime.UtcNow     | 28    | 14    |\n| 2 | File.ReadAllText    | 18    | 9     |\n| ...                                      |\n\n### Most Affected Files\n| File                          | Static Calls | Categories          |\n|-------------------------------|-------------|---------------------|\n| Services\u002FOrderProcessor.cs    | 12          | Time, FileSystem    |\n| ...                                                               |\n\n### Migration Priority\n1. **Time** (42 sites) — Use `TimeProvider`, zero NuGet dependencies on .NET 8+\n2. **File System** (31 sites) — Use `System.IO.Abstractions` NuGet package\n3. ...\n```\n\n### Step 5: Suggest next steps\n\nBased on the report, recommend which category to tackle first (highest count, best built-in support). Keep this to a few lines.\n\nMention `generate-testability-wrappers` or `migrate-static-to-wrapper` only when the user's next action clearly needs them — a hand-off note, not a sales pitch. Never end an audit with promotional next-steps that dilute the findings.\n\n## Validation\n\n- [ ] All `.cs` files in scope were scanned (check count)\n- [ ] Report includes category totals, top patterns, and affected files\n- [ ] Category totals, top patterns, and per-file counts reconcile to the same grand total\n- [ ] Files scanned, affected files, and call sites are reported as different quantities\n- [ ] Every aggregate was derived from one occurrence ledger rather than independently recounted\n- [ ] Every occurrence carries a `file:line` location\n- [ ] No findings are held outside the totals in an \"additional\" section\n- [ ] Calls on injected collaborators are excluded from the \"needs wrapping\" total\n- [ ] Deterministic pure helpers (`Path.Combine`, `Math.*`) are not counted as testability blockers\n- [ ] Each detected pattern has a recommended replacement listed\n- [ ] `obj\u002F` and `bin\u002F` directories were excluded\n- [ ] Migration priority is ordered by impact (count × ease of replacement)\n\n## Common Pitfalls\n\n| Pitfall | Solution |\n|---------|----------|\n| Scanning `obj\u002F` or generated code | Always exclude `obj\u002F`, `bin\u002F`, and `*.Designer.cs` |\n| Counting calls on injected collaborators | Trace the receiver: an injected `HttpClient`, `TimeProvider`, interface, or other caller-supplied dependency already has a seam and needs no replacement |\n| Missing statics inside lambdas\u002FLINQ | Search covers all code within `.cs` files, including lambdas |\n| Recommending `TimeProvider` on \u003C .NET 8 | Check `TargetFramework` in `.csproj` — if \u003C net8.0, recommend `NodaTime.IClock` or custom `ISystemClock` |\n| Ignoring test projects | Only scan production code — exclude `*.Tests.csproj` projects from the scan |\n| Under-counting by relegating findings | Real call sites belong in the category totals, not in a trailing \"also noticed\" paragraph that the totals ignore |\n| Calling an instance member a static | `new FileInfo(p).LastWriteTimeUtc` is an instance call but still a hidden file-system dependency — count it under File System and describe it accurately |\n| Recommending a wrapper for `Path.Combine` | Pure, deterministic helpers need no seam; listing them as blockers makes the recommendations wrong |\n","# Detect Static Dependencies\n\nScan a C# codebase for calls to hard-to-test static APIs and produce a ranked report showing which statics appear most frequently, which files are most affected, and which abstractions already exist in the .NET ecosystem to replace them.\n\n## When to Use\n\n- Auditing a project's testability before adding unit tests\n- Understanding the scope of static coupling in a legacy codebase\n- Prioritizing which statics to wrap first (highest-frequency wins)\n- Creating a migration plan for incremental testability improvements\n\n## Response Guidelines\n\n- Scale the response to the user's request. A question about a specific category (e.g., \"find time statics\") should focus on that category with file locations and counts, not produce a full report across all categories.\n- When the user provides a specific file or directory path, scan only that scope — do not expand to the entire solution unless asked.\n- The full structured report format in Step 4 is for comprehensive audit requests. For focused questions, return only the relevant subset (e.g., category summary + affected files for the requested category).\n\n## Execution Contract\n\n- A relative path named in the prompt is enough to start. Discover it with the\n  available file-listing tools and scan it immediately; do not ask the user to\n  provide or re-upload files before both discovery and a content search fail.\n- Start with a recursive, line-numbered content search over eligible `.cs`\n  files. Do not search only for the `static` keyword: ambient calls inside\n  LINQ expressions, lambdas, callbacks, and interpolated strings usually have\n  no `static` modifier.\n- If a file-reading tool fails on a path that listing or search proved exists,\n  classify the failure before retrying. Fall back to another available\n  mechanism such as `rg -n`, grep, or a shell file reader only for confirmed\n  tool availability, transport, or path-normalization failures and only after\n  verifying the canonical path remains inside the workspace. Stop on\n  content-exclusion, permission\u002Fpolicy, workspace-boundary, or unknown failures.\n  Search output can seed the occurrence ledger; open only the surrounding code\n  needed to verify receiver provenance.\n- Never stop after loading this skill or announcing a scan plan. Return the\n  completed audit in the same response. If every fallback genuinely fails,\n  report the verified partial findings and the exact limitation; do not invent\n  findings or replace the audit with a request to rerun.\n\n## When Not to Use\n\n- The user wants wrappers generated (hand off to `generate-testability-wrappers`)\n- The user wants mechanical migration done (hand off to `migrate-static-to-wrapper`)\n- The statics are already behind interfaces or `TimeProvider`\n- The code is not C# \u002F .NET\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Target path | No | A file, directory, project (.csproj), or solution (.sln) to scan. Defaults to the current workspace. |\n| Exclusion patterns | No | Glob patterns to skip (e.g., `**\u002Fobj\u002F**`, `**\u002FMigrations\u002F**`) |\n| Category filter | No | Limit to specific categories: `time`, `filesystem`, `environment`, `network`, `console`, `process` |\n\n## Workflow\n\n### Step 1: Determine scan scope\n\nResolve the target to a set of `.cs` files:\n- Treat a prompt-named workspace-relative path as the target; locate it rather\n  than asking the user for an absolute path.\n- If omitted, scan every eligible `.cs` file under the current workspace; do not\n  pick one project and silently omit its siblings.\n- If a `.cs` file, scan that single file.\n- If a directory, scan all `.cs` files recursively (excluding `obj\u002F`, `bin\u002F`).\n- If a `.csproj`, find its directory and scan `.cs` files within.\n- If a `.sln`, parse it, find all project directories, and scan `.cs` files across all projects.\n\nAlways exclude `obj\u002F`, `bin\u002F`, and any user-specified exclusion patterns.\n\n### Step 2: Search for static dependency patterns\n\nScan each file for calls matching these categories:\n\nTreat pattern matches as candidates, not findings. Before counting an instance call, trace how its\nreceiver enters the class. A collaborator supplied through a constructor, parameter, property, or\ndependency injection (DI) is already a test seam. In particular, an injected `HttpClient` is\ntestable with a controlled `HttpMessageHandler`; do not count its calls or recommend replacing it\nmerely because the injected type is concrete.\n\n| Category | Patterns to search for | Recommended replacement |\n|----------|----------------------|------------------------|\n| **Time** | `DateTime.Now`, `DateTime.UtcNow`, `DateTime.Today`, `DateTimeOffset.Now`, `DateTimeOffset.UtcNow`, `Task.Delay(`, `new CancellationTokenSource(TimeSpan` | `TimeProvider` (.NET 8+) |\n| **File System** | `File.ReadAllText(`, `File.WriteAllText(`, `File.Exists(`, `File.Delete(`, `File.Copy(`, `File.Move(`, `Directory.Exists(`, `Directory.CreateDirectory(`, `Directory.GetFiles(`, `Directory.Delete(`, `Path.GetTempPath(`, and instance members that hit the disk (`new FileInfo(...)`, `new DirectoryInfo(...)`, `.LastWriteTimeUtc`, `new StreamReader(path)`) | `IFileSystem` (System.IO.Abstractions NuGet) |\n| **Randomness \u002F identity** | `new Random(`, `Random.Shared`, `Guid.NewGuid(` | `TimeProvider`-style seam: inject `Random` \u002F an `IGuidProvider` |\n| **Culture \u002F serialization** | `CultureInfo.CurrentCulture`, `CultureInfo.CurrentUICulture`, `JsonSerializer.Serialize(`, `JsonSerializer.Deserialize(` | Pass culture\u002Foptions explicitly, or inject a serializer abstraction |\n| **Environment** | `Environment.GetEnvironmentVariable(`, `Environment.SetEnvironmentVariable(`, `Environment.MachineName`, `Environment.UserName`, `Environment.CurrentDirectory`, `Environment.Exit(` | Custom `IEnvironmentProvider` |\n| **Network** | `new HttpClient(`, `.GetAsync(`, `.PostAsync(`, `.SendAsync(` (confirm the receiver is an `HttpClient`; exclude calls whose receiver is injected or produced by an injected factory) | Inject `HttpClient` (commonly supplied by `IHttpClientFactory`) |\n| **Console** | `Console.WriteLine(`, `Console.ReadLine(`, `Console.Write(`, `Console.ReadKey(` | `IConsole` wrapper or `ILogger` |\n| **Process** | `Process.Start(`, `Process.GetCurrentProcess(`, `Process.GetProcessesByName(` | Custom `IProcessRunner` |\n\nFor time calls, inspect use as well as count. Two ambient clock reads in one\nlogical operation are two call sites and a consistency defect: for example,\nseparate `DateTime.UtcNow` reads for `CreatedAt` and\n`ExpiresAt = DateTime.UtcNow.AddDays(30)` can drift. Recommend one captured\ninstant. With `TimeProvider`, retain `DateTimeOffset` where possible; when the\nexisting member requires UTC `DateTime`, use `GetUtcNow().UtcDateTime`, never\n`.DateTime`, which loses the UTC kind. Treat capturing one instant as an\noptional behavior-level follow-up: a mechanical wrapper migration must preserve\nthe original reads one-for-one unless the user separately approves that\nsemantic change.\n\n### Step 3: Aggregate and rank results\n\nCount each call site across the entire scan scope — including the instance-member call sites covered by the rules below, not only `static` ones.\n\n**Counting rules — inaccurate totals are the main way this report loses to an ad-hoc scan:**\n\n- **Build one occurrence ledger before writing prose.** Give each included call\n  site exactly one row containing category, exact pattern, `file:line`, and\n  recommended seam. Derive every category, pattern, and per-file count by\n  grouping that same ledger; never recount independently while writing tables.\n- **Keep the three count domains separate.** `Files scanned` includes every\n  eligible source file; `affected files` includes only files with ledger rows;\n  `call sites` is the number of ledger rows. Never substitute one for another.\n- **One authoritative total.** Every call site you found belongs in the category summary and the grand total. Never park real findings in an \"additional observations\" section that the totals exclude.\n- **Classify by what the member touches, not by whether it is `static`.** Instance members that reach the same untestable resource still count and belong in the matching category (`new FileInfo(path).LastWriteTimeUtc` → File System; `new HttpClient().GetAsync(...)` → Network). Say \"hidden dependency\", not \"static\", when the member is an instance call.\n- **Check receiver provenance before counting instance calls.** Count a resource access only when the code under test acquires or constructs the dependency itself. Exclude constructor-, parameter-, property-, and DI-injected collaborators from the \"needs wrapping\" total, including concrete `HttpClient` instances.\n- **Exclude deterministic pure helpers from the \"needs wrapping\" total.** `Path.Combine`, `Path.GetExtension`, `Path.GetFileName`, and `Math.*`\u002F`string.*` statics take no ambient input and are trivially testable. List them, if at all, in a separate \"no action needed\" note — never as testability blockers.\n- **Cover every category before reporting** — time, file system, environment, network, console, process, randomness (`new Random()`, `Guid.NewGuid()`), culture (`CultureInfo.CurrentCulture`), and serialization\u002Fstatics such as `JsonSerializer`. Omitting a category that is present is an under-count.\n- **Give `file:line` for every occurrence** so the user can jump straight to it.\n- **Reconcile before publishing.** The category totals, the top-patterns table, and the per-file table must sum to the same grand total.\n- **Treat exclusions as a scope decision, not a category.** Remove `obj\u002F`,\n  `bin\u002F`, generated, and user-excluded files before building the ledger. Do not\n  include their files or call sites in any reported count. State the exclusions\n  once rather than mixing excluded candidates into the arithmetic.\n- **Label truncated rankings.** In a comprehensive audit, list all distinct\n  patterns when needed for reconciliation. If the user asked only for a top-N\n  subset, label it as a subset and do not imply that its rows sum to the grand\n  total.\n\nProduce a summary with:\n\n1. **Category summary** — total call sites per category (time, filesystem, env, etc.)\n2. **Top patterns** — the 10 most frequent individual patterns ranked by count\n3. **Most affected files** — files with the highest number of static dependencies\n4. **Existing abstractions available** — for each category, note the recommended .NET abstraction:\n   - Time → `TimeProvider` (built-in since .NET 8)\n   - File system → `System.IO.Abstractions` (NuGet package)\n   - HTTP → `IHttpClientFactory` (built-in)\n   - Environment → custom `IEnvironmentProvider`\n   - Console → custom `IConsole` or `ILogger`\n   - Process → custom `IProcessRunner`\n\n### Step 4: Present the report\n\nFormat the output as a structured report:\n\n```\n## Static Dependency Report\n\n**Scope**: \u003Cproject\u002Fsolution name>\n**Files scanned**: \u003Ccount>\n**Total static call sites**: \u003Ccount>\n\n### Category Summary\n| Category     | Call Sites | Recommended Abstraction |\n|-------------|-----------|------------------------|\n| Time         | 42        | TimeProvider (.NET 8+) |\n| File System  | 31        | System.IO.Abstractions |\n| Environment  | 12        | IEnvironmentProvider   |\n| ...          | ...       | ...                    |\n\n### Top 10 Patterns\n| # | Pattern             | Count | Files |\n|---|---------------------|-------|-------|\n| 1 | DateTime.UtcNow     | 28    | 14    |\n| 2 | File.ReadAllText    | 18    | 9     |\n| ...                                      |\n\n### Most Affected Files\n| File                          | Static Calls | Categories          |\n|-------------------------------|-------------|---------------------|\n| Services\u002FOrderProcessor.cs    | 12          | Time, FileSystem    |\n| ...                                                               |\n\n### Migration Priority\n1. **Time** (42 sites) — Use `TimeProvider`, zero NuGet dependencies on .NET 8+\n2. **File System** (31 sites) — Use `System.IO.Abstractions` NuGet package\n3. ...\n```\n\n### Step 5: Suggest next steps\n\nBased on the report, recommend which category to tackle first (highest count, best built-in support). Keep this to a few lines.\n\nMention `generate-testability-wrappers` or `migrate-static-to-wrapper` only when the user's next action clearly needs them — a hand-off note, not a sales pitch. Never end an audit with promotional next-steps that dilute the findings.\n\n## Validation\n\n- [ ] All `.cs` files in scope were scanned (check count)\n- [ ] Report includes category totals, top patterns, and affected files\n- [ ] Category totals, top patterns, and per-file counts reconcile to the same grand total\n- [ ] Files scanned, affected files, and call sites are reported as different quantities\n- [ ] Every aggregate was derived from one occurrence ledger rather than independently recounted\n- [ ] Every occurrence carries a `file:line` location\n- [ ] No findings are held outside the totals in an \"additional\" section\n- [ ] Calls on injected collaborators are excluded from the \"needs wrapping\" total\n- [ ] Deterministic pure helpers (`Path.Combine`, `Math.*`) are not counted as testability blockers\n- [ ] Each detected pattern has a recommended replacement listed\n- [ ] `obj\u002F` and `bin\u002F` directories were excluded\n- [ ] Migration priority is ordered by impact (count × ease of replacement)\n\n## Common Pitfalls\n\n| Pitfall | Solution |\n|---------|----------|\n| Scanning `obj\u002F` or generated code | Always exclude `obj\u002F`, `bin\u002F`, and `*.Designer.cs` |\n| Counting calls on injected collaborators | Trace the receiver: an injected `HttpClient`, `TimeProvider`, interface, or other caller-supplied dependency already has a seam and needs no replacement |\n| Missing statics inside lambdas\u002FLINQ | Search covers all code within `.cs` files, including lambdas |\n| Recommending `TimeProvider` on \u003C .NET 8 | Check `TargetFramework` in `.csproj` — if \u003C net8.0, recommend `NodaTime.IClock` or custom `ISystemClock` |\n| Ignoring test projects | Only scan production code — exclude `*.Tests.csproj` projects from the scan |\n| Under-counting by relegating findings | Real call sites belong in the category totals, not in a trailing \"also noticed\" paragraph that the totals ignore |\n| Calling an instance member a static | `new FileInfo(p).LastWriteTimeUtc` is an instance call but still a hidden file-system dependency — count it under File System and describe it accurately |\n| Recommending a wrapper for `Path.Combine` | Pure, deterministic helpers need no seam; listing them as blockers makes the recommendations wrong |\n",{"name":5,"license":15,"description":54},"ACTIVATION PREREQUISITE: the request or discovered target must explicitly identify C#, .NET, `.cs`, or `.csproj`; otherwise stay dormant without invoking this skill. USE FOR: locating System.DateTime.Now\u002FUtcNow, System.IO.File\u002FDirectory, System.Environment, HttpClient, Console, or Process usage in C#; auditing C# code for hard-to-test framework dependencies; or verifying those C# calls are already abstracted. DO NOT USE FOR: any target lacking the activation prerequisite; generating wrappers (use generate-testability-wrappers); migrating code (use migrate-static-to-wrapper); or general code review.\n","main","f8f2bfd77819bb789d4736ce7db4a567a37fcf7e",".agents\u002Fskills\u002Fdetect-static-dependencies",315,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx","https:\u002F\u002Fgithub.com\u002Fmicrosoft","https:\u002F\u002Fopensource.microsoft.com","Open source projects and samples from Microsoft","2026-11-14 14:01:43",{"source":8,"skillName":5,"commands":65,"prompt":75,"zipAvailable":76},[66,69,72],{"tool":67,"command":68},"npx","npx skills add microsoft\u002Ftestfx --skill detect-static-dependencies",{"tool":70,"command":71},"bunx","bunx skills add microsoft\u002Ftestfx --skill detect-static-dependencies",{"tool":73,"command":74},"pnpm","pnpm dlx skills add microsoft\u002Ftestfx --skill detect-static-dependencies","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fdetect-static-dependencies\u002FSKILL.md",true,[78,84,88,92],{"path":79,"resourceType":80,"resourceTypeDesc":81,"sizeBytes":82,"sourceHtmlUrl":83},"steps\u002Fstep-01-agent-loading.md","REFERENCE","参考文档",5172,"https:\u002F\u002Fgithub.com\u002Fsherryswift1019-gif\u002FAI-DZHT\u002Fblob\u002Fmain\u002F_bmad\u002Fcore\u002Fbmad-party-mode\u002Fsteps\u002Fstep-01-agent-loading.md",{"path":85,"resourceType":80,"resourceTypeDesc":81,"sizeBytes":86,"sourceHtmlUrl":87},"steps\u002Fstep-02-discussion-orchestration.md",6633,"https:\u002F\u002Fgithub.com\u002Fsherryswift1019-gif\u002FAI-DZHT\u002Fblob\u002Fmain\u002F_bmad\u002Fcore\u002Fbmad-party-mode\u002Fsteps\u002Fstep-02-discussion-orchestration.md",{"path":89,"resourceType":80,"resourceTypeDesc":81,"sizeBytes":90,"sourceHtmlUrl":91},"steps\u002Fstep-03-graceful-exit.md",7084,"https:\u002F\u002Fgithub.com\u002Fsherryswift1019-gif\u002FAI-DZHT\u002Fblob\u002Fmain\u002F_bmad\u002Fcore\u002Fbmad-party-mode\u002Fsteps\u002Fstep-03-graceful-exit.md",{"path":93,"resourceType":80,"resourceTypeDesc":81,"sizeBytes":94,"sourceHtmlUrl":95},"workflow.md",5444,"https:\u002F\u002Fgithub.com\u002Fsherryswift1019-gif\u002FAI-DZHT\u002Fblob\u002Fmain\u002F_bmad\u002Fcore\u002Fbmad-party-mode\u002Fworkflow.md",[97,123,148,169,189,214],{"skillNo":98,"name":99,"nameZh":6,"description":100,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":101,"categories":102,"keywords":103,"publishedAt":49},"01M70GYY0AV0JGMTHE2CN6TNYK","dotnet-breaking-changes","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).","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fdotnet-breaking-changes\u002FSKILL.md",[],[104,105,106,107,108,109,110,111,112,113,114,115,116,43,117,118,119,120,121,122],"dotnet","dotnet breaking changes","changes","green","project","default","local","errors","interface","generated","upgrade","invoke","you","refactoring","merge","rename","library","live","assembly",{"skillNo":124,"name":125,"nameZh":6,"description":126,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":127,"categories":128,"keywords":130,"publishedAt":49},"01M699HHE9DTZWAMPB1ZR7ZKSW","testfx-test-value-evidence","Apply the vendored test-value-evidence skill with TestFx toolchain, assertion, parallel-safety, packed-consumer, and review conventions. Use when implementing or reviewing behavior-changing TestFx PRs or commits, including source-only changes.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftestfx-test-value-evidence\u002FSKILL.md",[129],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[46,131,132,133,134,47,135,136,137,138,139,140,141,142,143,144,106,145,146,147],"testfx test value evidence","test value evidence","evidence","value","parallel","toolchain","commits","implementing","prs","only","use","when","apply","with","behavior","Safety","conventions",{"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":49},"01M699HEYWH1B9XGNBFY8J2FZS","testfx-parallel-safety-preflight","Check concurrency and isolation before implementing or refactoring TestFx tests, lifecycle code, helpers, shared-state declarations, or production paths those tests exercise. Use before narrowing DoNotParallelize or ResourceLock, changing parallelization settings, or introducing asynchronous workers; repeat against the final diff.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftestfx-parallel-safety-preflight\u002FSKILL.md",[],[155,146,156,135,157,158,138,159,43,160,117,161,162,163,164,165,166,167,141,168],"testfx parallel safety preflight","preflight","shared","asynchronous","exercise","concurrency","paths","isolation","helpers","diff","parallelization","lifecycle","tests","workers",{"skillNo":170,"name":171,"nameZh":6,"description":172,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":173,"categories":174,"keywords":175,"publishedAt":49},"01M699HC61ZV9GJ2C8JV03951G","testfx-localization-validation","Validate generated TestFx localization catalogs, resource IDs, placeholders and locked tokens. Use for RESX\u002FXLF changes, OneLoc PRs, localization build failures, duplicate targets or units, and orphaned translation entries.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftestfx-localization-validation\u002FSKILL.md",[],[176,177,178,179,180,181,182,183,184,139,113,141,185,186,106,187,188],"testfx localization validation","validation","localization","validate","translation","ids","for","tokens","entries","units","duplicate","targets","build",{"skillNo":190,"name":191,"nameZh":6,"description":192,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":193,"categories":194,"keywords":196,"publishedAt":49},"01M699H9J2NWQXE4BDT6R0FH7S","testfx-acceptance-validation","Validate TestFx shipping paths and capable CI execution using packed consumers, package\u002Fcache provenance, exact exits and artifacts, and selected-versus-executed test evidence. Use for acceptance regressions, package\u002FMSBuild\u002Flauncher\u002Fprotocol changes, or skipped-only CI results; not for unrelated unit-only or documentation changes.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftestfx-acceptance-validation\u002FSKILL.md",[195],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[197,198,177,46,199,133,200,201,202,140,203,141,204,205,206,207,208,209,210,211,212,213,161,182],"testfx acceptance validation","acceptance","msbuild","launcher","ci","execution","package","cache","artifacts","provenance","documentation","unrelated","shipping","results","protocol","unit","ci results",{"skillNo":215,"name":216,"nameZh":6,"description":217,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":218,"categories":219,"keywords":220,"publishedAt":49},"01M699H70BB999BT4F627T1PHE","session-delivery-hygiene","Gate local-session scope, persistent delivery, live PR state, integration provenance, and owned cleanup. Use before implementing guidance changes, changing or pushing PR work, diagnosing package\u002Foutput mismatches, or handing off implementation.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Ftestfx\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fsession-delivery-hygiene\u002FSKILL.md",[],[221,222,223,224,225,138,110,226,227,121,228,203,141,206,229,230,106,231,232,233],"session delivery hygiene","delivery","session","hygiene","scope","cleanup","work","pr","output","state","integration","guidance","gate"]