@cratis/pi 7.101.0 → 7.103.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/package/corpus/harnesses/copilot/instructions/github-actions.instructions.md +17 -0
  2. package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +10 -1
  3. package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +2 -0
  4. package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +4 -0
  5. package/package/corpus/hooks/README.md +11 -1
  6. package/package/corpus/hooks/scripts/cratis-check-pr.mjs +12 -1
  7. package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +79 -23
  8. package/package/corpus/hooks/scripts/cratis-guard-writes.sh +145 -40
  9. package/package/corpus/rules/github-actions.md +17 -0
  10. package/package/corpus/rules/profiles.md +10 -1
  11. package/package/corpus/rules/pull-requests.md +2 -0
  12. package/package/corpus/rules/secrets.md +4 -0
  13. package/package/corpus/skills/analyzing-dotnet-performance/LICENSE +23 -0
  14. package/package/corpus/skills/analyzing-dotnet-performance/NOTICE +36 -0
  15. package/package/corpus/skills/analyzing-dotnet-performance/SKILL.md +214 -0
  16. package/package/corpus/skills/analyzing-dotnet-performance/references/async-patterns.md +122 -0
  17. package/package/corpus/skills/analyzing-dotnet-performance/references/collections-and-linq.md +220 -0
  18. package/package/corpus/skills/analyzing-dotnet-performance/references/critical-patterns.md +291 -0
  19. package/package/corpus/skills/analyzing-dotnet-performance/references/io-and-serialization.md +135 -0
  20. package/package/corpus/skills/analyzing-dotnet-performance/references/memory-and-strings.md +219 -0
  21. package/package/corpus/skills/analyzing-dotnet-performance/references/regex-patterns.md +95 -0
  22. package/package/corpus/skills/analyzing-dotnet-performance/references/structural-patterns.md +38 -0
  23. package/package/corpus/skills/binlog-generation/LICENSE +23 -0
  24. package/package/corpus/skills/binlog-generation/NOTICE +22 -0
  25. package/package/corpus/skills/binlog-generation/SKILL.md +129 -0
  26. package/package/corpus/skills/check-bin-obj-clash/LICENSE +23 -0
  27. package/package/corpus/skills/check-bin-obj-clash/NOTICE +27 -0
  28. package/package/corpus/skills/check-bin-obj-clash/SKILL.md +127 -0
  29. package/package/corpus/skills/check-bin-obj-clash/references/common-causes-and-fixes.md +206 -0
  30. package/package/corpus/skills/coverage-analysis/LICENSE +23 -0
  31. package/package/corpus/skills/coverage-analysis/NOTICE +32 -0
  32. package/package/corpus/skills/coverage-analysis/SKILL.md +198 -0
  33. package/package/corpus/skills/coverage-analysis/references/guidelines.md +68 -0
  34. package/package/corpus/skills/coverage-analysis/references/output-format.md +91 -0
  35. package/package/corpus/skills/coverage-analysis/references/report-generation.md +58 -0
  36. package/package/corpus/skills/coverage-analysis/references/setup-discovery.md +307 -0
  37. package/package/corpus/skills/coverage-analysis/scripts/Compute-CrapScores.ps1 +80 -0
  38. package/package/corpus/skills/coverage-analysis/scripts/CoverageData.ps1 +272 -0
  39. package/package/corpus/skills/coverage-analysis/scripts/Extract-MethodCoverage.ps1 +65 -0
  40. package/package/corpus/skills/cratis-ci-pipeline-performance/LICENSE +2 -0
  41. package/package/corpus/skills/cratis-ci-pipeline-performance/SKILL.md +201 -0
  42. package/package/corpus/skills/cratis-ci-pipeline-performance/references/build-once-and-shard.md +158 -0
  43. package/package/corpus/skills/cratis-ci-pipeline-performance/references/caching.md +86 -0
  44. package/package/corpus/skills/cratis-ci-pipeline-performance/references/flaky-and-hung-jobs.md +70 -0
  45. package/package/corpus/skills/cratis-ci-pipeline-performance/references/publish-pipelines.md +67 -0
  46. package/package/corpus/skills/cratis-ci-pipeline-performance/references/required-checks-and-path-filters.md +117 -0
  47. package/package/corpus/skills/cratis-ci-pipeline-performance/references/runner-sizing-and-images.md +65 -0
  48. package/package/corpus/skills/cratis-ci-pipeline-performance/references/security-and-pinning.md +73 -0
  49. package/package/corpus/skills/cratis-ci-pipeline-performance/references/sources.md +33 -0
  50. package/package/corpus/skills/cratis-ci-pipeline-performance/references/test-setup-cost.md +82 -0
  51. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/ci-timings.sh +356 -0
  52. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1003.json +79 -0
  53. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1007.json +79 -0
  54. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1010.json +79 -0
  55. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/runs.json +102 -0
  56. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/LICENSE +2 -0
  57. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/SKILL.md +244 -0
  58. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/docker-images.md +86 -0
  59. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/dotnet-nuget.md +87 -0
  60. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/go-jvm-python-elixir.md +83 -0
  61. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/npm-yarn.md +78 -0
  62. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/other-environments.md +60 -0
  63. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/worked-examples.md +97 -0
  64. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/cratis-local-packages.sh +778 -0
  65. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/npm-restore-check.cjs +109 -0
  66. package/package/corpus/skills/csharp-refactoring/LICENSE +23 -0
  67. package/package/corpus/skills/csharp-refactoring/NOTICE +22 -0
  68. package/package/corpus/skills/csharp-refactoring/SKILL.md +175 -0
  69. package/package/corpus/skills/csharp-refactoring/references/operation-catalog.md +37 -0
  70. package/package/corpus/skills/directory-build-organization/LICENSE +23 -0
  71. package/package/corpus/skills/directory-build-organization/NOTICE +21 -0
  72. package/package/corpus/skills/directory-build-organization/SKILL.md +205 -0
  73. package/package/corpus/skills/directory-build-organization/references/common-patterns.md +56 -0
  74. package/package/corpus/skills/directory-build-organization/references/multi-level-examples.md +167 -0
  75. package/package/corpus/skills/directory-build-organization/references/targetframework-props-pitfall.md +54 -0
  76. package/package/corpus/skills/dotnet-aot-compat/LICENSE +23 -0
  77. package/package/corpus/skills/dotnet-aot-compat/NOTICE +18 -0
  78. package/package/corpus/skills/dotnet-aot-compat/SKILL.md +265 -0
  79. package/package/corpus/skills/dotnet-aot-compat/references/polyfills.md +43 -0
  80. package/package/corpus/skills/dotnet-trace-collect/LICENSE +23 -0
  81. package/package/corpus/skills/dotnet-trace-collect/NOTICE +65 -0
  82. package/package/corpus/skills/dotnet-trace-collect/SKILL.md +116 -0
  83. package/package/corpus/skills/dotnet-trace-collect/references/dotnet-monitor.md +97 -0
  84. package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect-linux.md +84 -0
  85. package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect.md +91 -0
  86. package/package/corpus/skills/dotnet-trace-collect/references/perfcollect.md +200 -0
  87. package/package/corpus/skills/dotnet-trace-collect/references/perfview.md +141 -0
  88. package/package/corpus/skills/dotnet-trace-collect/references/tool-selection.md +160 -0
  89. package/package/corpus/skills/dump-collect/LICENSE +23 -0
  90. package/package/corpus/skills/dump-collect/NOTICE +40 -0
  91. package/package/corpus/skills/dump-collect/SKILL.md +97 -0
  92. package/package/corpus/skills/dump-collect/references/container-dumps.md +412 -0
  93. package/package/corpus/skills/dump-collect/references/coreclr-dumps.md +108 -0
  94. package/package/corpus/skills/dump-collect/references/nativeaot-dumps.md +280 -0
  95. package/package/corpus/skills/eliminate-redundant-restores/LICENSE +23 -0
  96. package/package/corpus/skills/eliminate-redundant-restores/NOTICE +19 -0
  97. package/package/corpus/skills/eliminate-redundant-restores/SKILL.md +112 -0
  98. package/package/corpus/skills/extension-points/LICENSE +23 -0
  99. package/package/corpus/skills/extension-points/NOTICE +18 -0
  100. package/package/corpus/skills/extension-points/SKILL.md +248 -0
  101. package/package/corpus/skills/filter-syntax/LICENSE +23 -0
  102. package/package/corpus/skills/filter-syntax/NOTICE +17 -0
  103. package/package/corpus/skills/filter-syntax/SKILL.md +179 -0
  104. package/package/corpus/skills/including-generated-files/LICENSE +23 -0
  105. package/package/corpus/skills/including-generated-files/NOTICE +17 -0
  106. package/package/corpus/skills/including-generated-files/SKILL.md +139 -0
  107. package/package/corpus/skills/microbenchmarking/LICENSE +23 -0
  108. package/package/corpus/skills/microbenchmarking/NOTICE +36 -0
  109. package/package/corpus/skills/microbenchmarking/SKILL.md +131 -0
  110. package/package/corpus/skills/microbenchmarking/references/bdn-internals-and-tuning.md +143 -0
  111. package/package/corpus/skills/microbenchmarking/references/comparison-strategies.md +200 -0
  112. package/package/corpus/skills/microbenchmarking/references/diagnosers-and-exporters.md +150 -0
  113. package/package/corpus/skills/microbenchmarking/references/project-setup-and-running.md +189 -0
  114. package/package/corpus/skills/microbenchmarking/references/writing-benchmarks.md +310 -0
  115. package/package/corpus/skills/msbuild/LICENSE +23 -0
  116. package/package/corpus/skills/msbuild/NOTICE +55 -0
  117. package/package/corpus/skills/msbuild/SKILL.md +117 -0
  118. package/package/corpus/skills/msbuild/references/antipatterns/additional-antipatterns.md +291 -0
  119. package/package/corpus/skills/msbuild/references/antipatterns/incremental-build-inputs-outputs.md +57 -0
  120. package/package/corpus/skills/msbuild/references/antipatterns/private-assets.md +29 -0
  121. package/package/corpus/skills/msbuild/references/binlog-failure-analysis.md +85 -0
  122. package/package/corpus/skills/msbuild/references/binlog-generation.md +69 -0
  123. package/package/corpus/skills/msbuild/references/build-perf-baseline.md +493 -0
  124. package/package/corpus/skills/msbuild/references/build-perf-diagnostics.md +268 -0
  125. package/package/corpus/skills/msbuild/references/copy-to-output-directory.md +183 -0
  126. package/package/corpus/skills/msbuild/references/eval-performance.md +190 -0
  127. package/package/corpus/skills/msbuild/references/extension-points.md +281 -0
  128. package/package/corpus/skills/msbuild/references/incremental-build.md +365 -0
  129. package/package/corpus/skills/msbuild/references/msbuild-antipatterns.md +401 -0
  130. package/package/corpus/skills/orleans/LICENSE +21 -0
  131. package/package/corpus/skills/orleans/NOTICE +64 -0
  132. package/package/corpus/skills/orleans/SKILL.md +140 -0
  133. package/package/corpus/skills/orleans/references/anti-patterns.md +660 -0
  134. package/package/corpus/skills/orleans/references/configuration-api.md +933 -0
  135. package/package/corpus/skills/orleans/references/examples.md +66 -0
  136. package/package/corpus/skills/orleans/references/grain-api.md +784 -0
  137. package/package/corpus/skills/orleans/references/grains.md +75 -0
  138. package/package/corpus/skills/orleans/references/hosting.md +71 -0
  139. package/package/corpus/skills/orleans/references/implementation.md +591 -0
  140. package/package/corpus/skills/orleans/references/official-docs-index.md +250 -0
  141. package/package/corpus/skills/orleans/references/patterns.md +811 -0
  142. package/package/corpus/skills/orleans/references/persistence-api.md +495 -0
  143. package/package/corpus/skills/orleans/references/primitive-selection.md +35 -0
  144. package/package/corpus/skills/orleans/references/scheduling-and-services.md +295 -0
  145. package/package/corpus/skills/orleans/references/serialization-api.md +299 -0
  146. package/package/corpus/skills/orleans/references/streaming-api.md +396 -0
  147. package/package/corpus/skills/orleans/references/testing-patterns.md +210 -0
  148. package/package/corpus/skills/platform-detection/LICENSE +23 -0
  149. package/package/corpus/skills/platform-detection/NOTICE +17 -0
  150. package/package/corpus/skills/platform-detection/SKILL.md +230 -0
  151. package/package/corpus/skills/platform-detection/references/command-mode.md +27 -0
  152. package/package/corpus/skills/profiling/LICENSE +21 -0
  153. package/package/corpus/skills/profiling/NOTICE +50 -0
  154. package/package/corpus/skills/profiling/SKILL.md +144 -0
  155. package/package/corpus/skills/profiling/references/commands.md +439 -0
  156. package/package/corpus/skills/profiling/references/patterns.md +586 -0
  157. package/package/corpus/skills/profiling/references/profiling.md +190 -0
  158. package/package/corpus/skills/run-tests/LICENSE +23 -0
  159. package/package/corpus/skills/run-tests/NOTICE +19 -0
  160. package/package/corpus/skills/run-tests/SKILL.md +305 -0
  161. package/package/corpus/skills/target-authoring/LICENSE +23 -0
  162. package/package/corpus/skills/target-authoring/NOTICE +17 -0
  163. package/package/corpus/skills/target-authoring/SKILL.md +165 -0
  164. package/package/corpus/skills/vectorization/LICENSE +23 -0
  165. package/package/corpus/skills/vectorization/NOTICE +17 -0
  166. package/package/corpus/skills/vectorization/SKILL.md +189 -0
  167. package/package/profile-catalog.json +87 -1
  168. package/package.json +3 -2
@@ -0,0 +1,268 @@
1
+ # Diagnosing MSBuild Build Performance
2
+
3
+ ## Contents
4
+
5
+ - Performance Analysis Methodology
6
+ - Key Metrics and Thresholds
7
+ - Common Bottlenecks
8
+ - Using Binlog Replay for Performance Analysis
9
+ - Quick Wins Checklist
10
+ - Impact Categorization
11
+
12
+ Source: original build perf diagnostics skill.
13
+
14
+ ## Performance Analysis Methodology
15
+
16
+ 1. Establish the relevant cold, warm changed-input, or no-change
17
+ [baseline](build-perf-baseline.md). Keep `Rebuild` and ordinary `Build` distinct.
18
+ 2. Use an existing matching binlog first. If one is missing, follow
19
+ [binlog generation](binlog-generation.md) without changing the reported scenario.
20
+ 3. [Replay the binary log](binlog-failure-analysis.md#replay-a-binary-log) locally
21
+ to a diagnostic text log with a performance summary. The searches below assume
22
+ that log is named `full-01.log`, as in the replay command; use its actual path.
23
+ 4. Find expensive projects, targets, and tasks, then inspect the corresponding
24
+ instance, execution reason, inputs, and dependency waits. Select one cause
25
+ for a controlled change, not every plausible optimization.
26
+
27
+ Performance summaries report cumulative/inclusive durations, not additive shares
28
+ of elapsed wall time or CPU usage. Parallel work overlaps; orchestration targets
29
+ and tasks can include child work and waits.
30
+
31
+ In particular, **`ResolveProjectReferences` can mostly represent waiting for
32
+ dependent builds**, while a yielded node performs other work. Do not optimize it
33
+ solely because it tops the target summary. Follow the dependencies to actual
34
+ `Csc`, `ResolveAssemblyReference`, `Copy`, or custom-task work. Even `MSBuild`
35
+ task duration can include child builds rather than exclusive self-time.
36
+
37
+ ## Key Metrics and Thresholds
38
+
39
+ Use these as investigation hints, not universal budgets or proof of a cause:
40
+
41
+ - **Build duration:** historical rough examples are under 10 seconds for a small
42
+ workload, 60 seconds for a medium one, and 5 minutes for a large one. Compare
43
+ the repository's actual scenario and machine, not labels alone.
44
+ - **Node utilization:** activity below roughly 80% warrants checking dependency
45
+ waits, limited parallel work, and resource contention. Text-summary totals
46
+ alone cannot establish utilization.
47
+ - **Single-target domination:** investigate a target near or above 50% of elapsed
48
+ build time, but separate inclusive waits and nested work.
49
+ - **Analyzer versus compiler time:** around 30% or more of `Csc` time deserves
50
+ investigation. Analyzer timings can overlap; do not add them as wall time or
51
+ remove diagnostics solely to reach a percentage.
52
+ - **RAR time:** over 5 seconds per project, especially over 15 seconds, merits
53
+ examination of reference count, paths, and resolution work.
54
+
55
+ ## Common Bottlenecks
56
+
57
+ ### 1. ResolveAssemblyReference (RAR) Slowness
58
+
59
+ - **Symptoms:** substantial `ResolveAssemblyReference` task time, for example
60
+ over 5 seconds per affected project.
61
+ - **Root causes:** many assembly references, network-based paths, or expensive
62
+ assembly search paths.
63
+ - **Fixes:** remove demonstrated unused references/search paths and avoid
64
+ unnecessary network resolution, preserving all consumers.
65
+ - **Diagnostic guardrail:** `DesignTimeBuild`, `ResolveAssemblyReferencesSilent`,
66
+ and `ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch` affect build or
67
+ diagnostic behavior; changing them is not an established RAR performance fix.
68
+ - **Key insight:** RAR can legitimately execute on no-change builds to account
69
+ for external resolution state, including installed targeting packs or GAC
70
+ assemblies. Do not force it to skip using an incomplete freshness check.
71
+ - **Reduce transitive references carefully:** `DisableTransitiveProjectReferences`
72
+ is a last resort requiring explicit references to consumed dependencies.
73
+ `ReferenceOutputAssembly="false"` is appropriate when only build ordering is
74
+ needed, but still retains that ordering. Trim only unused package references.
75
+ Use the [dependency-graph audit](build-perf-baseline.md#step-4-dependency-graph-trimming).
76
+
77
+ ### 2. Roslyn Analyzers and Source Generators
78
+
79
+ - **Symptoms:** `Csc` takes much longer than a comparable compilation, sometimes
80
+ more than twice a measured compiler-only scenario.
81
+ - **Diagnosis:** inspect `Csc` and analyzer/generator timing in the replayed log.
82
+ If timing detail is absent, obtain compiler-supported diagnostic timing rather
83
+ than guessing from package count. A controlled `-p:RunAnalyzers=false`
84
+ comparison must compile the same inputs on both sides, not compare a no-op
85
+ against forced compilation.
86
+ - **Conditional development policy:** after measuring and agreeing the policy,
87
+ examples include:
88
+
89
+ ```xml
90
+ <PropertyGroup>
91
+ <RunAnalyzers Condition="'$(ContinuousIntegrationBuild)' != 'true'">false</RunAnalyzers>
92
+ <EnforceCodeStyleInBuild Condition="'$(ContinuousIntegrationBuild)' == 'true'">true</EnforceCodeStyleInBuild>
93
+ </PropertyGroup>
94
+ ```
95
+
96
+ - **Per-configuration:** if development Debug builds are the intended scope, also
97
+ guard the CI case, for example
98
+ `Condition="'$(Configuration)' == 'Debug' and '$(ContinuousIntegrationBuild)' != 'true'"`.
99
+ CI can build Debug too.
100
+ - **Rule/package scope:** configure genuinely redundant or expensive diagnostics
101
+ appropriately, including `.editorconfig` severity policy, instead of blindly
102
+ removing analyzer packages.
103
+ - **Key principle:** preserve required analyzer enforcement in CI. Verify effective
104
+ properties and that the pipeline actually sets the CI signal. A conditional
105
+ assignment does not undo an earlier or later unconditional override.
106
+ - **Source generators:** do not assume `RunAnalyzers=false` disables or measures
107
+ every generator. Preserve required generated code and inspect generator-specific
108
+ evidence.
109
+ - **GlobalPackageReference:** examine the project, `Directory.Build.props`,
110
+ `Directory.Packages.props`, and package imports. Active global analyzer
111
+ references can affect production and test projects; a declaration alone does
112
+ not prove restoration or inclusion in the compiler's analyzer list.
113
+ - **EnforceCodeStyleInBuild:** when enabled it adds code-style checks to compiler
114
+ execution; it does not mean analyzers execute when compilation itself skips.
115
+ Conditionalize only under an agreed development/CI policy.
116
+
117
+ ### 3. Serialization Bottlenecks (Single-threaded Targets)
118
+
119
+ - **Symptoms:** one project dominates while other nodes are idle or waiting.
120
+ - **Common culprits:** a long dependency chain, a critical-path project, or custom
121
+ orchestration issuing independent project requests sequentially.
122
+ - **Fixes:** optimize the measured critical path, remove false dependencies, or
123
+ split a bottleneck only when it exposes independent work. In custom `MSBuild`
124
+ tasks, `BuildInParallel="true"` also needs multiple available nodes.
125
+ - Follow [parallel build tuning](build-perf-baseline.md#step-6-parallel-build-tuning).
126
+ More nodes cannot parallelize required serial dependencies.
127
+
128
+ ### 4. Excessive File I/O (Copy Tasks)
129
+
130
+ - **Symptoms:** high Copy aggregate time or excessive copy invocations.
131
+ - **Root causes:** thousands of unnecessary files, network destinations, or
132
+ accidental per-item task batching instead of an appropriate item batch.
133
+ - **First fixes:** inspect item metadata, source/destination paths, and invocation
134
+ counts. Reduce only unnecessary copies and choose the correct
135
+ [copy mode](copy-to-output-directory.md); replacing `Always` blindly with
136
+ `PreserveNewest` can leave mutated destination files stale.
137
+ - **Skip unchanged files:** inspect whether the actual target consumes
138
+ `SkipCopyUnchangedFiles`; it may already be true. Custom `Copy` tasks expose
139
+ `SkipUnchangedFiles`. Neither setting is a content-hash comparison.
140
+ - **Hardlinks:** identify the hot copy target in the binlog, then set only the
141
+ property that target passes to `Copy.UseHardlinksIfPossible`:
142
+ `CreateHardLinksForCopyFilesToOutputDirectoryIfPossible` for primary build
143
+ outputs, `CreateHardLinksForAdditionalFilesIfPossible` for content/additional
144
+ files, or `CreateHardLinksForCopyLocalIfPossible` for copy-local references.
145
+ Custom `Copy` tasks need their own `UseHardlinksIfPossible` value. Hardlinks
146
+ can help compatible same-volume, immutable-file workloads. A destination
147
+ mutation through a hardlink can modify its source, so do not use this for
148
+ writable/resettable content.
149
+ - **Shared layout:** `UseCommonOutputDirectory=true` assumes a genuinely shared,
150
+ collision-free output layout. Verify ownership and consumers first.
151
+ [Artifacts output](build-perf-baseline.md#step-2-artifacts-output-layout), including
152
+ `--artifacts-path`, requires SDK 8+ and is a separate measured layout change.
153
+ - **Dev Drive:** on supported Windows development or self-hosted CI machines,
154
+ evaluate Dev Drive for a measured I/O bottleneck. Do not promise automatic
155
+ copy-on-write gains for every task or disable security tooling for a benchmark.
156
+
157
+ ### 5. Evaluation Overhead
158
+
159
+ - **Symptoms:** significant startup/evaluation time before target execution.
160
+ - **Root causes:** expensive `Directory.Build.props` logic, globs traversing
161
+ large trees, imports, or SDK-resolution overhead. NuGet-based SDK resolver
162
+ latency depends on the toolset and environment; historical timings are not a
163
+ fixed per-project cost.
164
+ - **Fixes:** simplify only measured costly logic and narrow offending globs.
165
+ `EnableDefaultItems=false` is a last resort requiring complete intentional item
166
+ lists, not the first optimization. Changing SDK resolution must preserve the
167
+ build's actual SDK contract.
168
+ - Use [evaluation performance](eval-performance.md) for focused evidence and fixes.
169
+
170
+ ### 6. NuGet Restore in Build
171
+
172
+ - **Symptoms:** restore contributes substantial work repeatedly. Distinguish an
173
+ up-to-date check from actual repeated network/dependency-resolution work.
174
+ - **Separate restore from build:** successfully restore matching inputs, then
175
+ use `dotnet build --no-restore`. Keep build-only and restore-inclusive baselines
176
+ distinct; dependency changes require fresh assets.
177
+ - **Static graph restore:** `RestoreUseStaticGraphEvaluation=true` in
178
+ `Directory.Build.props` is a candidate for a measured large restore graph.
179
+ Validate equivalent resolved assets and toolset support. It is distinct from
180
+ graph build and its benefit is workload-dependent.
181
+ - Investigate changing inputs and feed/cache behavior rather than purging shared
182
+ package caches.
183
+
184
+ ### 7. Large Project Count and Graph Shape
185
+
186
+ - **Symptoms:** many tiny projects add evaluation/invocation overhead, or deep
187
+ dependency chains serialize otherwise parallelizable work.
188
+ - **Consider:** graph mode for scheduling, consolidating small leaves for measured
189
+ overhead, or splitting a large bottleneck into independent pieces. These are
190
+ different remedies for different evidence.
191
+ - **Graph shape matters:** wide independent branches expose parallelism; a long
192
+ required chain remains serial regardless of node count.
193
+ - **Actions:** identify the exact unnecessary edge or costly project. Preserve
194
+ direct API/metadata contracts and compare the resulting critical path.
195
+ [Static graph builds](build-perf-baseline.md#step-5-static-graph-builds-graph)
196
+ do not automatically provide isolation or a cache.
197
+
198
+ ## Using Binlog Replay for Performance Analysis
199
+
200
+ 1. [Replay the existing binlog](binlog-failure-analysis.md#replay-a-binary-log)
201
+ with diagnostic verbosity and a performance summary.
202
+ 2. Read the target/task summaries, usually near the end of the replayed log:
203
+
204
+ ```powershell
205
+ Select-String -Path .\full-01.log -Pattern 'Target Performance Summary|Task Performance Summary' -Context 0,50
206
+ ```
207
+
208
+ Use the cumulative timings to select candidates, not to compute additive
209
+ percentages of elapsed build time.
210
+ 3. Find per-project timings and instances:
211
+
212
+ ```powershell
213
+ Select-String -Path .\full-01.log -Pattern 'Done Building Project|Project Performance Summary' -Context 0,20
214
+ ```
215
+
216
+ 4. Inspect recorded scheduling evidence:
217
+
218
+ ```powershell
219
+ Select-String -Path .\full-01.log -Pattern 'node.*assigned|building with|scheduler'
220
+ ```
221
+
222
+ These messages alone are not a utilization measurement. Report missing
223
+ scheduling/timestamp detail rather than inferring an exact timeline.
224
+ 5. Check analyzer timing, when the original capture contains it:
225
+
226
+ ```powershell
227
+ Select-String -Path .\full-01.log -Pattern 'Total analyzer execution time|analyzer.*elapsed|CompilerAnalyzerDriver'
228
+ ```
229
+
230
+ 6. Drill into the relevant project's slow target and its tasks:
231
+
232
+ ```powershell
233
+ Select-String -Path .\full-01.log -Pattern 'Target "CoreCompile"|Target "ResolveAssemblyReferences"' -Context 0,20
234
+ ```
235
+
236
+ ## Quick Wins Checklist
237
+
238
+ Choose only items supported by the diagnosis, one experiment at a time:
239
+
240
+ - [ ] Confirm the intended `-m` node budget rather than assuming host defaults.
241
+ - [ ] Separate restore and build only with current matching assets.
242
+ - [ ] Measure static graph restore and verify equivalent assets.
243
+ - [ ] Consider hardlinks only for compatible immutable-file workloads.
244
+ - [ ] Scope analyzer changes to an agreed development policy; retain CI enforcement.
245
+ - [ ] Inspect `ProduceReferenceAssembly` before enabling it; many SDK configurations
246
+ already enable it. Verify implementation-only and public-API changes downstream.
247
+ - [ ] Check [incremental behavior](incremental-build.md) on an ordinary second build.
248
+ - [ ] Inspect evaluated output/intermediate paths for collisions between projects,
249
+ configurations, TFMs, and RIDs; do not just give everything one common directory.
250
+ - [ ] Measure graph mode for a statically discoverable multi-project build.
251
+ - [ ] Evaluate SDK 8+ artifacts layout only with its path-consumer checks.
252
+ - [ ] Consider a separate Dev Drive environment experiment for Windows I/O limits.
253
+
254
+ ## Impact Categorization
255
+
256
+ - **HIGH IMPACT:** a verified critical-path cost above roughly 10% of elapsed
257
+ build time, or substantial real work behind a target reported above 50%.
258
+ - **MEDIUM IMPACT:** verified costs around 2-10%.
259
+ - **QUICK WINS:** low-effort changes with demonstrated modest benefit, not
260
+ unmeasured property toggles.
261
+
262
+ Report the project/TFM, target/task, evidence, one targeted change, and a
263
+ same-scenario before/after comparison with spread. Verify required artifacts,
264
+ diagnostics, and relevant tests/consumers. Stop if the result is unmeasured, within
265
+ noise, or breaks correctness; do not claim success from disabled validation or
266
+ inclusive timing arithmetic.
267
+
268
+ > **Cratis:** Only as a labelled timing experiment; restore analyzers and warnings-as-errors and run the real gate before finishing.
@@ -0,0 +1,183 @@
1
+ # Choosing a CopyToOutputDirectory Mode
2
+
3
+ ## Contents
4
+
5
+ - Overview
6
+ - Why `Always` Is Usually the Wrong Choice
7
+ - `IfDifferent`: Copy When Different, in Either Direction
8
+ - Globally Softening `Always` with `$(SkipUnchangedFilesOnCopyAlways)`
9
+ - How the Modes Flow Through the Build
10
+ - Version Requirement
11
+ - Quick Decision Guide
12
+ - Verify the Choice
13
+
14
+ Source: original copy to output directory skill.
15
+
16
+ ## Overview
17
+
18
+ `CopyToOutputDirectory` metadata controls whether an item such as `Content`,
19
+ `None`, `EmbeddedResource`, or `Compile` is copied next to the build output.
20
+ `CopyToPublishDirectory` controls the corresponding publish selection. Picking
21
+ the wrong mode can leave stale files or add recurring copy work.
22
+
23
+ As of **MSBuild 17.13 / .NET SDK 9.0.2xx**, the common build targets support four
24
+ values. Check the publishing SDK separately for publish behavior.
25
+
26
+ | Mode | Copies when | Incremental cost | Typical use |
27
+ | --- | --- | --- | --- |
28
+ | `Never` | Never through this copy pipeline | None | Files not needed in that output |
29
+ | `PreserveNewest` | Source is newer than destination, or destination is missing | Timestamp checks | Normal source-edited content |
30
+ | `Always` | Every build that reaches the copy target, unless a skip-unchanged override is enabled | Repeated copies in classic mode | A real unconditional-copy/reset requirement |
31
+ | `IfDifferent` | Timestamp or size differs in either direction, or destination is missing | Timestamp and size checks | Destination may be mutated between builds |
32
+
33
+ Omitted metadata commonly means `Never`, but SDKs/imports can supply item-specific
34
+ defaults. Inspect effective metadata before assuming omission disables copying.
35
+
36
+ The following `Include` examples are for explicitly included files. When an SDK
37
+ already includes a file, use `Update` on its actual item type instead; for example,
38
+ a web SDK can classify `appsettings.json` as `Content`, not `None`.
39
+
40
+ ```xml
41
+ <ItemGroup>
42
+ <None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
43
+ <None Include="testdata\seed.db" CopyToOutputDirectory="IfDifferent" />
44
+ </ItemGroup>
45
+ ```
46
+
47
+ The child-element form is equivalent:
48
+
49
+ ```xml
50
+ <None Include="testdata\seed.db">
51
+ <CopyToOutputDirectory>IfDifferent</CopyToOutputDirectory>
52
+ </None>
53
+ ```
54
+
55
+ ## Why `Always` Is Usually the Wrong Choice
56
+
57
+ Classic `Always` re-copies files whenever that copy pipeline executes, including
58
+ otherwise no-change MSBuild builds. Many or large content files can make this a
59
+ measurable recurring cost. An IDE up-to-date check that skips MSBuild entirely is
60
+ a separate decision.
61
+
62
+ Historically, `Always` addressed a specific scenario: **the destination changes
63
+ between builds**. Tests or applications might mutate a database, state file, or
64
+ configuration file. With `PreserveNewest`, a newer destination is not reset
65
+ because the source is no longer newer. `Always` forces a reset, but pays for it
66
+ even when the destination has not changed.
67
+
68
+ Do not replace `Always` solely because it appears in project text. First measure
69
+ copy work and establish whether unconditional copying is part of the required
70
+ behavior.
71
+
72
+ ## `IfDifferent`: Copy When Different, in Either Direction
73
+
74
+ `IfDifferent` restores the source when the destination is missing or its timestamp
75
+ or size differs, whether the destination is newer or older. It skips unchanged
76
+ files according to that metadata comparison.
77
+
78
+ `_CopyDifferingSourceItemsToOutputDirectory` uses the `Copy` task with
79
+ `SkipUnchangedFiles="true"`. This is a **timestamp-and-size heuristic, not a content
80
+ hash**. A destination edited to the same size and last-write timestamp is treated
81
+ as unchanged and is not restored.
82
+
83
+ Use `IfDifferent` when:
84
+
85
+ - Tests or the application mutate copied databases, state/storage files, or
86
+ editable configuration, and the metadata-based reset meets the contract.
87
+ - `Always` was used only to synchronize a drifting destination, not because
88
+ every build must perform a fresh copy.
89
+
90
+ ```xml
91
+ <ItemGroup>
92
+ <!-- For an explicitly included fixture; use Update if already included -->
93
+ <None Include="fixtures\catalog.db" CopyToOutputDirectory="IfDifferent" />
94
+ </ItemGroup>
95
+ ```
96
+
97
+ If same-size/same-timestamp mutations must also be reset, retain genuine
98
+ unconditional copying or a separately verified content-based mechanism.
99
+
100
+ ## Globally Softening `Always` with `$(SkipUnchangedFilesOnCopyAlways)`
101
+
102
+ For a codebase with many legacy `Always` items, a bulk opt-in is:
103
+
104
+ ```xml
105
+ <PropertyGroup>
106
+ <SkipUnchangedFilesOnCopyAlways>true</SkipUnchangedFilesOnCopyAlways>
107
+ </PropertyGroup>
108
+ ```
109
+
110
+ The common `Always` copy target then passes `SkipUnchangedFiles="true"` to `Copy`,
111
+ giving those items the same timestamp/size skip heuristic as `IfDifferent`.
112
+
113
+ - Unset/default behavior is effectively `false`: classic `Always` copying.
114
+ - Setting it in `Directory.Build.props` can affect the entire repository.
115
+ - Prefer per-item intent when practical. A bulk property avoids individual item
116
+ edits, but still changes behavior and requires checking every affected reset
117
+ contract.
118
+ - Do not assume this build-target property governs every publish copy task.
119
+
120
+ ## How the Modes Flow Through the Build
121
+
122
+ `GetCopyToOutputDirectoryItems` buckets items by their metadata. The common
123
+ `_CopySourceItemsToOutputDirectory` pipeline, invoked by
124
+ `CopyFilesToOutputDirectory`, uses:
125
+
126
+ - `_CopyOutOfDateSourceItemsToOutputDirectory` for `PreserveNewest`, with
127
+ `Inputs`/`Outputs` timestamp checks.
128
+ - `_CopyOutOfDateSourceItemsToOutputDirectoryAlways` for `Always`, with the
129
+ skip-unchanged property controlling its `Copy` task.
130
+ - `_CopyDifferingSourceItemsToOutputDirectory` for `IfDifferent`, with
131
+ `SkipUnchangedFiles="true"`.
132
+
133
+ These standard build copies register `FileWrites` for the normal Clean pipeline.
134
+ Custom tasks and publish outputs can have different tracking; inspect ownership
135
+ and use only approved, scoped cleanup.
136
+
137
+ **Transitive copy:** copyable items can flow to referencing projects through
138
+ `ProjectReference`, including `_CopyToOutputDirectoryTransitiveItems`; `Never`
139
+ does not participate in that copy pipeline. Verify consumers as well as the
140
+ defining project. Supporting targets also collect `IfDifferent` for ClickOnce;
141
+ test the publishing mode and supported toolset actually used by the repository.
142
+
143
+ ## Version Requirement
144
+
145
+ `IfDifferent` and `SkipUnchangedFilesOnCopyAlways` require **MSBuild 17.13+**
146
+ (**.NET SDK 9.0.2xx+ / Visual Studio 2022 17.13+**) for the common build-target
147
+ behavior described here. Older common targets can silently omit an item whose
148
+ mode they do not recognize.
149
+
150
+ Check the actual SDK/MSBuild host and the minimum supported toolset, not the
151
+ target framework. Gate usage or require a compatible SDK through `global.json`,
152
+ and check Visual Studio/MSBuild.exe hosts too. On older hosts, retaining `Always`
153
+ can preserve required reset behavior; substituting `PreserveNewest` may not.
154
+ An unsupported skip-unchanged property is not evidence of a speedup.
155
+
156
+ `CopyToPublishDirectory` is implemented by publishing targets. Verify recognition,
157
+ transitive collection, and reset behavior on the selected publishing SDK rather
158
+ than inferring support from the common build-target version alone.
159
+
160
+ ## Quick Decision Guide
161
+
162
+ - Not needed in this output: explicitly choose `Never` when defaults might copy it.
163
+ - Normal source-edited file: `PreserveNewest`.
164
+ - Mutated destination should reset when metadata differs: `IfDifferent`.
165
+ - Fresh copy required on every pipeline execution, including metadata-identical
166
+ changes: `Always`, without a skip-unchanged override.
167
+ - Many compatible legacy `Always` items: consider
168
+ `SkipUnchangedFilesOnCopyAlways=true` after checking the same behavioral limits.
169
+
170
+ ## Verify the Choice
171
+
172
+ In an approved disposable workspace or on explicitly scoped files, check an
173
+ ordinary no-change build, a source edit, a missing destination, and destination
174
+ mutations with newer and older timestamps. Check same-size/same-timestamp mutation
175
+ when strict reset matters. Also test source addition/removal and the intended
176
+ stale-output policy: copy metadata alone does not guarantee obsolete destinations
177
+ are deleted.
178
+
179
+ Check build, referencing projects, runtime use, and relevant publish/ClickOnce
180
+ outputs. Do not hardlink writable/resettable outputs to sources: writes through a
181
+ hardlink can change the source. Compare copy counts and same-scenario timings
182
+ using the [baseline](build-perf-baseline.md); preserve the output contract rather
183
+ than claiming success merely because fewer files were copied.
@@ -0,0 +1,190 @@
1
+ # Diagnosing MSBuild Evaluation Performance
2
+
3
+ ## Contents
4
+
5
+ - Confirm the Problem Before Changing Anything
6
+ - MSBuild Evaluation Phases
7
+ - Diagnosing Evaluation Performance
8
+ - Expensive Glob Patterns
9
+ - Import Chain Analysis
10
+ - Multiple Evaluations
11
+ - TreatAsLocalProperty
12
+ - Property Function Cost
13
+ - Optimization Checklist
14
+
15
+ Source: original eval performance skill.
16
+
17
+ Evaluation is work MSBuild does before executing targets: reading project files,
18
+ processing imports, expanding globs, and establishing definitions. Find and
19
+ confirm the bottleneck first; recommend a change only when measurements justify it.
20
+
21
+ ## Confirm the Problem Before Changing Anything
22
+
23
+ Engage only when evaluation is **measurably** costly. Do not act when:
24
+
25
+ - **Slowness is in compilation or target execution.** Use
26
+ [build performance diagnostics](build-perf-diagnostics.md).
27
+ - **The complaint is unnecessary rebuilding.** Use
28
+ [incremental-build diagnosis](incremental-build.md).
29
+ - **There is no measurement.** Reuse a matching binlog or timing summary, or
30
+ collect one through [binlog generation](binlog-generation.md). Do not infer
31
+ slowness solely from project syntax.
32
+ - **A pattern below appears but evaluation is already fast.** Broad globs, deep
33
+ imports, and item-default settings are not reasons to rewrite healthy builds.
34
+
35
+ Report unmeasured patterns as observations, not causes. Prefer the smallest
36
+ targeted change; never disable SDK defaults as the first move.
37
+
38
+ ## MSBuild Evaluation Phases
39
+
40
+ 1. **Initial properties:** environment variables, global properties, and reserved properties.
41
+ 2. **Imports and properties:** process imports and property groups in evaluation order.
42
+ 3. **Item definitions:** establish metadata defaults.
43
+ 4. **Items:** process `Include`, `Remove`, `Update`, and glob expansion.
44
+ 5. **UsingTask definitions:** register task implementations.
45
+ 6. **Target definitions:** establish targets for subsequent execution.
46
+
47
+ Evaluation precedes execution for a project instance. It can be expensive even
48
+ when compilation later skips; a source generator running in `Csc` is execution,
49
+ not project evaluation.
50
+
51
+ ## Diagnosing Evaluation Performance
52
+
53
+ ### Using Binlog Replay
54
+
55
+ 1. [Replay the existing binary log](binlog-failure-analysis.md#replay-a-binary-log)
56
+ locally with diagnostic verbosity and performance summaries. The search below uses that
57
+ command's `full-01.log`; substitute the actual path if you chose another name.
58
+ 2. Look for evaluation timing and instance information:
59
+
60
+ ```powershell
61
+ Select-String -Path .\full-01.log -Pattern 'Project Evaluation Performance Summary|Evaluation started|Evaluation finished|Project evaluation' -Context 0,20
62
+ ```
63
+
64
+ 3. Record the expensive project instance, global properties, configuration,
65
+ TFM/RID, and invocation purpose. Distinct instances may be expected rather
66
+ than duplicate work.
67
+ 4. Inspect its imports and property/item definitions. If the capture lacks
68
+ detailed evaluation timing, do not fabricate it from the number of imports.
69
+ On supporting MSBuild versions, `-profileEvaluation:eval.tsv` provides a
70
+ focused profile; record the extra instrumentation separately.
71
+
72
+ ### Using /pp (Preprocess)
73
+
74
+ ```powershell
75
+ dotnet msbuild .\MyProject.csproj "-pp:full.xml"
76
+ ```
77
+
78
+ - Supply the same configuration/TFM/global properties as the affected instance.
79
+ - `/pp` inlines imports and shows their boundaries and definitions.
80
+ - It does **not** fully expand every property/item value or measure evaluation cost.
81
+ - Over 10,000 lines can be a clue to inspect, not proof of heavy evaluation.
82
+
83
+ For evaluated values, **MSBuild 17.8+** also supports queries such as:
84
+
85
+ ```powershell
86
+ dotnet msbuild .\MyProject.csproj -p:TargetFramework=net8.0 -getProperty:Configuration,TargetFramework -getItem:Compile
87
+ ```
88
+
89
+ Without a target request, this evaluates rather than builds. On older hosts, use
90
+ the available diagnostic evidence instead of assuming these query switches exist.
91
+
92
+ ### Using /clp:PerformanceSummary
93
+
94
+ Add `-clp:PerformanceSummary` to the same build invocation for evaluation,
95
+ project, target, and task summaries. Keep evaluation separate from execution,
96
+ and remember that cumulative/nested/parallel durations are not additive wall time.
97
+
98
+ ## Expensive Glob Patterns
99
+
100
+ Pursue these remedies only when item evaluation is slow and the globs are the
101
+ measured cause; a custom glob over a small intended tree is fine.
102
+
103
+ - Broad patterns such as `**\*.cs` can traverse large trees.
104
+ - SDK default globs have exclusions; custom globs may not share them.
105
+ - Watch for unexpected traversal of `node_modules`, `.git`, and generated
106
+ `bin`/`obj` trees, not just the text of the pattern.
107
+ - Append appropriate `DefaultItemExcludes` rather than replacing existing
108
+ exclusions. A custom item must actually consume the exclusion property.
109
+ - Narrow roots/file types where equivalent, such as `src\**\*.cs`.
110
+ - `EnableDefaultItems=false` is a last resort requiring an intentional
111
+ replacement for compile/resource/content discovery and future file changes.
112
+ - Inspect evaluated `Compile` and other affected items for unexpected files.
113
+
114
+ For an offending custom item, adapt exclusions and paths to the intended input set:
115
+
116
+ ```xml
117
+ <PropertyGroup>
118
+ <DefaultItemExcludes>$(DefaultItemExcludes);**\node_modules\**;**\.git\**</DefaultItemExcludes>
119
+ </PropertyGroup>
120
+ <ItemGroup>
121
+ <AdditionalFiles Include="src\**\*.json" Exclude="$(DefaultItemExcludes)" />
122
+ </ItemGroup>
123
+ ```
124
+
125
+ Verify item equivalence and file addition/removal behavior, not just a faster scan.
126
+
127
+ ## Import Chain Analysis
128
+
129
+ - Deep chains, for example over 20 levels, are candidates to inspect, not
130
+ inherently slow builds.
131
+ - Imports require reading, parsing, and evaluation; measure the costly portion.
132
+ - Common sources include package `.props`/`.targets`, framework SDK imports,
133
+ and `Directory.Build` chains.
134
+ - Use `/pp` import-boundary comments and file paths to understand ownership and
135
+ nesting; do not depend on one exact comment phrase.
136
+ - Consolidate/remove imports or package-provided build content only when the
137
+ chain is measurably costly and all property/item/target consumers are preserved.
138
+
139
+ ## Multiple Evaluations
140
+
141
+ - The same project path can legitimately have multiple evaluations.
142
+ - Different global-property sets, TFM/RID/configurations, outer/inner
143
+ multi-targeting builds, restore, and design-time builds can require distinct
144
+ instances.
145
+ - Compare instance identity and call purpose, not just an evaluation count or a
146
+ rule of "one evaluation per project per TFM."
147
+ - Investigate callers that introduce accidental global-property differences.
148
+ Normalize only differences that do not change intended behavior.
149
+ - [Graph build](build-perf-baseline.md#step-5-static-graph-builds-graph) is an
150
+ experiment for compatible graphs, not a guarantee of evaluation deduplication.
151
+
152
+ ## TreatAsLocalProperty
153
+
154
+ - Allows listed global properties to be overridden within the current project.
155
+ - A local override is not automatically forwarded as a new global value to
156
+ child projects. For example, a command-line value can still reach a child even
157
+ though the parent locally changed that property.
158
+ - It is therefore not a blanket mechanism for removing inherited properties
159
+ from child builds. Use explicit `MSBuild` task property controls, such as
160
+ `Properties` or `RemoveProperties`, when that is the intended contract.
161
+ - Use it when local override semantics are genuinely needed. The number of names
162
+ listed is not by itself evidence of measurable evaluation overhead.
163
+
164
+ ## Property Function Cost
165
+
166
+ - Property functions used during evaluation run at evaluation time; expressions
167
+ inside targets instead run when that execution reaches them.
168
+ - Most string operations are cheap.
169
+ - `$([System.IO.File]::ReadAllText(...))` can repeat file I/O on each evaluation.
170
+ - Network access and heavy computations can be costly; measure the actual
171
+ function rather than declaring every file read a bottleneck.
172
+ - Keep evaluation-time functions fast and side-effect-free. Move work into a
173
+ correctly incremental target only if evaluation-time consumers do not need
174
+ its result.
175
+
176
+ ## Optimization Checklist
177
+
178
+ - [ ] Inspect `/pp` definitions/imports without treating line count as timing.
179
+ - [ ] Explain evaluation counts by project-instance identity.
180
+ - [ ] Exclude large unintended directories from measured costly globs.
181
+ - [ ] Reduce measured unnecessary evaluation-time I/O.
182
+ - [ ] Simplify expensive import chains while preserving consumers.
183
+ - [ ] Compare graph mode only where statically discoverable and relevant.
184
+ - [ ] Remove `UsingTask` declarations only if their consumers and measurable cost
185
+ justify it.
186
+
187
+ Repeat the same build scenario and evaluate the same intended item/property sets.
188
+ Check additions, changes, removals, affected configurations, and downstream
189
+ behavior. Report timing and evidence before/after; leave configuration unchanged
190
+ when evaluation is cheap, unmeasured, or the apparent gain is within noise.