@cratis/pi 7.102.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.
- package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +10 -1
- package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +2 -0
- package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +4 -0
- package/package/corpus/hooks/README.md +11 -1
- package/package/corpus/hooks/scripts/cratis-check-pr.mjs +12 -1
- package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +79 -23
- package/package/corpus/hooks/scripts/cratis-guard-writes.sh +145 -40
- package/package/corpus/rules/profiles.md +10 -1
- package/package/corpus/rules/pull-requests.md +2 -0
- package/package/corpus/rules/secrets.md +4 -0
- package/package/corpus/skills/analyzing-dotnet-performance/LICENSE +23 -0
- package/package/corpus/skills/analyzing-dotnet-performance/NOTICE +36 -0
- package/package/corpus/skills/analyzing-dotnet-performance/SKILL.md +214 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/async-patterns.md +122 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/collections-and-linq.md +220 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/critical-patterns.md +291 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/io-and-serialization.md +135 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/memory-and-strings.md +219 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/regex-patterns.md +95 -0
- package/package/corpus/skills/analyzing-dotnet-performance/references/structural-patterns.md +38 -0
- package/package/corpus/skills/binlog-generation/LICENSE +23 -0
- package/package/corpus/skills/binlog-generation/NOTICE +22 -0
- package/package/corpus/skills/binlog-generation/SKILL.md +129 -0
- package/package/corpus/skills/check-bin-obj-clash/LICENSE +23 -0
- package/package/corpus/skills/check-bin-obj-clash/NOTICE +27 -0
- package/package/corpus/skills/check-bin-obj-clash/SKILL.md +127 -0
- package/package/corpus/skills/check-bin-obj-clash/references/common-causes-and-fixes.md +206 -0
- package/package/corpus/skills/coverage-analysis/LICENSE +23 -0
- package/package/corpus/skills/coverage-analysis/NOTICE +32 -0
- package/package/corpus/skills/coverage-analysis/SKILL.md +198 -0
- package/package/corpus/skills/coverage-analysis/references/guidelines.md +68 -0
- package/package/corpus/skills/coverage-analysis/references/output-format.md +91 -0
- package/package/corpus/skills/coverage-analysis/references/report-generation.md +58 -0
- package/package/corpus/skills/coverage-analysis/references/setup-discovery.md +307 -0
- package/package/corpus/skills/coverage-analysis/scripts/Compute-CrapScores.ps1 +80 -0
- package/package/corpus/skills/coverage-analysis/scripts/CoverageData.ps1 +272 -0
- package/package/corpus/skills/coverage-analysis/scripts/Extract-MethodCoverage.ps1 +65 -0
- package/package/corpus/skills/csharp-refactoring/LICENSE +23 -0
- package/package/corpus/skills/csharp-refactoring/NOTICE +22 -0
- package/package/corpus/skills/csharp-refactoring/SKILL.md +175 -0
- package/package/corpus/skills/csharp-refactoring/references/operation-catalog.md +37 -0
- package/package/corpus/skills/directory-build-organization/LICENSE +23 -0
- package/package/corpus/skills/directory-build-organization/NOTICE +21 -0
- package/package/corpus/skills/directory-build-organization/SKILL.md +205 -0
- package/package/corpus/skills/directory-build-organization/references/common-patterns.md +56 -0
- package/package/corpus/skills/directory-build-organization/references/multi-level-examples.md +167 -0
- package/package/corpus/skills/directory-build-organization/references/targetframework-props-pitfall.md +54 -0
- package/package/corpus/skills/dotnet-aot-compat/LICENSE +23 -0
- package/package/corpus/skills/dotnet-aot-compat/NOTICE +18 -0
- package/package/corpus/skills/dotnet-aot-compat/SKILL.md +265 -0
- package/package/corpus/skills/dotnet-aot-compat/references/polyfills.md +43 -0
- package/package/corpus/skills/dotnet-trace-collect/LICENSE +23 -0
- package/package/corpus/skills/dotnet-trace-collect/NOTICE +65 -0
- package/package/corpus/skills/dotnet-trace-collect/SKILL.md +116 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-monitor.md +97 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect-linux.md +84 -0
- package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect.md +91 -0
- package/package/corpus/skills/dotnet-trace-collect/references/perfcollect.md +200 -0
- package/package/corpus/skills/dotnet-trace-collect/references/perfview.md +141 -0
- package/package/corpus/skills/dotnet-trace-collect/references/tool-selection.md +160 -0
- package/package/corpus/skills/dump-collect/LICENSE +23 -0
- package/package/corpus/skills/dump-collect/NOTICE +40 -0
- package/package/corpus/skills/dump-collect/SKILL.md +97 -0
- package/package/corpus/skills/dump-collect/references/container-dumps.md +412 -0
- package/package/corpus/skills/dump-collect/references/coreclr-dumps.md +108 -0
- package/package/corpus/skills/dump-collect/references/nativeaot-dumps.md +280 -0
- package/package/corpus/skills/eliminate-redundant-restores/LICENSE +23 -0
- package/package/corpus/skills/eliminate-redundant-restores/NOTICE +19 -0
- package/package/corpus/skills/eliminate-redundant-restores/SKILL.md +112 -0
- package/package/corpus/skills/extension-points/LICENSE +23 -0
- package/package/corpus/skills/extension-points/NOTICE +18 -0
- package/package/corpus/skills/extension-points/SKILL.md +248 -0
- package/package/corpus/skills/filter-syntax/LICENSE +23 -0
- package/package/corpus/skills/filter-syntax/NOTICE +17 -0
- package/package/corpus/skills/filter-syntax/SKILL.md +179 -0
- package/package/corpus/skills/including-generated-files/LICENSE +23 -0
- package/package/corpus/skills/including-generated-files/NOTICE +17 -0
- package/package/corpus/skills/including-generated-files/SKILL.md +139 -0
- package/package/corpus/skills/microbenchmarking/LICENSE +23 -0
- package/package/corpus/skills/microbenchmarking/NOTICE +36 -0
- package/package/corpus/skills/microbenchmarking/SKILL.md +131 -0
- package/package/corpus/skills/microbenchmarking/references/bdn-internals-and-tuning.md +143 -0
- package/package/corpus/skills/microbenchmarking/references/comparison-strategies.md +200 -0
- package/package/corpus/skills/microbenchmarking/references/diagnosers-and-exporters.md +150 -0
- package/package/corpus/skills/microbenchmarking/references/project-setup-and-running.md +189 -0
- package/package/corpus/skills/microbenchmarking/references/writing-benchmarks.md +310 -0
- package/package/corpus/skills/msbuild/LICENSE +23 -0
- package/package/corpus/skills/msbuild/NOTICE +55 -0
- package/package/corpus/skills/msbuild/SKILL.md +117 -0
- package/package/corpus/skills/msbuild/references/antipatterns/additional-antipatterns.md +291 -0
- package/package/corpus/skills/msbuild/references/antipatterns/incremental-build-inputs-outputs.md +57 -0
- package/package/corpus/skills/msbuild/references/antipatterns/private-assets.md +29 -0
- package/package/corpus/skills/msbuild/references/binlog-failure-analysis.md +85 -0
- package/package/corpus/skills/msbuild/references/binlog-generation.md +69 -0
- package/package/corpus/skills/msbuild/references/build-perf-baseline.md +493 -0
- package/package/corpus/skills/msbuild/references/build-perf-diagnostics.md +268 -0
- package/package/corpus/skills/msbuild/references/copy-to-output-directory.md +183 -0
- package/package/corpus/skills/msbuild/references/eval-performance.md +190 -0
- package/package/corpus/skills/msbuild/references/extension-points.md +281 -0
- package/package/corpus/skills/msbuild/references/incremental-build.md +365 -0
- package/package/corpus/skills/msbuild/references/msbuild-antipatterns.md +401 -0
- package/package/corpus/skills/orleans/LICENSE +21 -0
- package/package/corpus/skills/orleans/NOTICE +64 -0
- package/package/corpus/skills/orleans/SKILL.md +140 -0
- package/package/corpus/skills/orleans/references/anti-patterns.md +660 -0
- package/package/corpus/skills/orleans/references/configuration-api.md +933 -0
- package/package/corpus/skills/orleans/references/examples.md +66 -0
- package/package/corpus/skills/orleans/references/grain-api.md +784 -0
- package/package/corpus/skills/orleans/references/grains.md +75 -0
- package/package/corpus/skills/orleans/references/hosting.md +71 -0
- package/package/corpus/skills/orleans/references/implementation.md +591 -0
- package/package/corpus/skills/orleans/references/official-docs-index.md +250 -0
- package/package/corpus/skills/orleans/references/patterns.md +811 -0
- package/package/corpus/skills/orleans/references/persistence-api.md +495 -0
- package/package/corpus/skills/orleans/references/primitive-selection.md +35 -0
- package/package/corpus/skills/orleans/references/scheduling-and-services.md +295 -0
- package/package/corpus/skills/orleans/references/serialization-api.md +299 -0
- package/package/corpus/skills/orleans/references/streaming-api.md +396 -0
- package/package/corpus/skills/orleans/references/testing-patterns.md +210 -0
- package/package/corpus/skills/platform-detection/LICENSE +23 -0
- package/package/corpus/skills/platform-detection/NOTICE +17 -0
- package/package/corpus/skills/platform-detection/SKILL.md +230 -0
- package/package/corpus/skills/platform-detection/references/command-mode.md +27 -0
- package/package/corpus/skills/profiling/LICENSE +21 -0
- package/package/corpus/skills/profiling/NOTICE +50 -0
- package/package/corpus/skills/profiling/SKILL.md +144 -0
- package/package/corpus/skills/profiling/references/commands.md +439 -0
- package/package/corpus/skills/profiling/references/patterns.md +586 -0
- package/package/corpus/skills/profiling/references/profiling.md +190 -0
- package/package/corpus/skills/run-tests/LICENSE +23 -0
- package/package/corpus/skills/run-tests/NOTICE +19 -0
- package/package/corpus/skills/run-tests/SKILL.md +305 -0
- package/package/corpus/skills/target-authoring/LICENSE +23 -0
- package/package/corpus/skills/target-authoring/NOTICE +17 -0
- package/package/corpus/skills/target-authoring/SKILL.md +165 -0
- package/package/corpus/skills/vectorization/LICENSE +23 -0
- package/package/corpus/skills/vectorization/NOTICE +17 -0
- package/package/corpus/skills/vectorization/SKILL.md +189 -0
- package/package/profile-catalog.json +85 -1
- package/package.json +1 -1
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
# MSBuild anti-pattern catalog
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- AP-01: Exec for operations with built-in tasks
|
|
6
|
+
- AP-02: Unquoted string comparisons
|
|
7
|
+
- AP-03: Hardcoded absolute paths
|
|
8
|
+
- AP-04: Restating SDK defaults
|
|
9
|
+
- AP-05: Redundant file lists in SDK-style projects
|
|
10
|
+
- AP-06: Package DLL HintPaths instead of package references
|
|
11
|
+
- AP-07: Build-only packages exposed to consumers
|
|
12
|
+
- AP-08: Copy-pasted shared properties
|
|
13
|
+
- AP-09: Unintended package-version drift
|
|
14
|
+
- AP-10: Monolithic targets
|
|
15
|
+
- AP-11: Untracked artifact-producing targets
|
|
16
|
+
- AP-12: Defaults defined after their consumers
|
|
17
|
+
- AP-13: Missing guards on optional imports
|
|
18
|
+
- AP-14: Backslashes where path normalization does not apply
|
|
19
|
+
- AP-15: Unexpected property overrides across scopes
|
|
20
|
+
|
|
21
|
+
Based on the original msbuild antipatterns skill. The numbered catalog retains its
|
|
22
|
+
smell, impact, and correction structure. Use relevant entries for troubleshooting or an
|
|
23
|
+
explicit authoring review, not as a mandate to rewrite every matching pattern.
|
|
24
|
+
|
|
25
|
+
Confirm evaluated behavior and exceptions before editing. Preserve framework/package versions,
|
|
26
|
+
intentional overrides, item metadata, and public/output contracts. Legacy project-system
|
|
27
|
+
migration is a separate task, not an automatic consequence of finding verbose XML.
|
|
28
|
+
|
|
29
|
+
## AP-01: Exec for operations with built-in tasks
|
|
30
|
+
|
|
31
|
+
**Smell:** shell `mkdir`, `copy`, or `del` commands in a target.
|
|
32
|
+
|
|
33
|
+
**Impact:** shell-specific syntax, weak file-level logging, and more difficult incremental
|
|
34
|
+
tracking. Built-in tasks integrate with MSBuild, but using one does not automatically make the
|
|
35
|
+
containing target incremental.
|
|
36
|
+
|
|
37
|
+
```xml
|
|
38
|
+
<!-- BAD: shell-dependent file operations. -->
|
|
39
|
+
<Target Name="PrepareOutput">
|
|
40
|
+
<Exec Command="mkdir $(OutputPath)logs" />
|
|
41
|
+
<Exec Command="copy config.json $(OutputPath)" />
|
|
42
|
+
<Exec Command="del $(IntermediateOutputPath)*.tmp" />
|
|
43
|
+
</Target>
|
|
44
|
+
|
|
45
|
+
<!-- GOOD: preserve the intended operation with built-in tasks. -->
|
|
46
|
+
<Target Name="PrepareOutput">
|
|
47
|
+
<MakeDir Directories="$(OutputPath)logs" />
|
|
48
|
+
<Copy SourceFiles="config.json" DestinationFolder="$(OutputPath)" />
|
|
49
|
+
<ItemGroup>
|
|
50
|
+
<TempFiles Include="$(IntermediateOutputPath)*.tmp" />
|
|
51
|
+
</ItemGroup>
|
|
52
|
+
<Delete Files="@(TempFiles)" />
|
|
53
|
+
</Target>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
| Shell operation | MSBuild task |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `mkdir` | `MakeDir` |
|
|
59
|
+
| `copy` / `cp` / recursive copy | `Copy`, with the intended item set |
|
|
60
|
+
| `del` / `rm` | `Delete`, scoped to known owned files |
|
|
61
|
+
| `move` / `mv` | `Move` |
|
|
62
|
+
| Write text | `WriteLinesToFile` |
|
|
63
|
+
| Update a timestamp | `Touch` |
|
|
64
|
+
|
|
65
|
+
Keep real external tools as tools, with correct quoting, platform support, and error handling.
|
|
66
|
+
Preserve copy/reset semantics and never widen the deletion scope during this conversion.
|
|
67
|
+
|
|
68
|
+
## AP-02: Unquoted string comparisons
|
|
69
|
+
|
|
70
|
+
**Smell:** `Condition="$(Foo) == Bar"`.
|
|
71
|
+
|
|
72
|
+
**Impact:** empty values or values containing spaces/special characters can change parsing.
|
|
73
|
+
|
|
74
|
+
```xml
|
|
75
|
+
<!-- BAD -->
|
|
76
|
+
<PropertyGroup Condition="$(Configuration) == Release">
|
|
77
|
+
<Optimize>true</Optimize>
|
|
78
|
+
</PropertyGroup>
|
|
79
|
+
|
|
80
|
+
<!-- GOOD -->
|
|
81
|
+
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
|
82
|
+
<Optimize>true</Optimize>
|
|
83
|
+
</PropertyGroup>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Quote both string operands. Do not flag valid boolean/function conditions merely because they
|
|
87
|
+
are not string comparisons.
|
|
88
|
+
|
|
89
|
+
## AP-03: Hardcoded absolute paths
|
|
90
|
+
|
|
91
|
+
**Smell:** machine-specific tool or import paths.
|
|
92
|
+
|
|
93
|
+
**Impact:** builds fail on another checkout, machine, or operating system.
|
|
94
|
+
|
|
95
|
+
```xml
|
|
96
|
+
<!-- BAD -->
|
|
97
|
+
<PropertyGroup>
|
|
98
|
+
<ToolPath>C:\tools\mytool\mytool.exe</ToolPath>
|
|
99
|
+
</PropertyGroup>
|
|
100
|
+
<Import Project="C:\repos\shared\common.props" />
|
|
101
|
+
|
|
102
|
+
<!-- GOOD: RepoRoot must be defined by the repository. -->
|
|
103
|
+
<PropertyGroup>
|
|
104
|
+
<ToolPath>$(MSBuildThisFileDirectory)tools\mytool\mytool.exe</ToolPath>
|
|
105
|
+
</PropertyGroup>
|
|
106
|
+
<Import Project="$(RepoRoot)eng\common.props" />
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
| Property/function | Meaning |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| `MSBuildThisFileDirectory` | Directory of the file defining the current import/logic |
|
|
112
|
+
| `MSBuildProjectDirectory` | Directory of the project being built |
|
|
113
|
+
| `GetDirectoryNameOfFileAbove` / `GetPathOfFileAbove` | Find a repository marker or parent file |
|
|
114
|
+
| `NormalizePath` / `NormalizeDirectory` | Combine and normalize path segments |
|
|
115
|
+
|
|
116
|
+
Resolve relative paths against the intended owner, not an incidental caller working directory.
|
|
117
|
+
|
|
118
|
+
## AP-04: Restating SDK defaults
|
|
119
|
+
|
|
120
|
+
**Smell:** properties repeat the selected SDK's defaults.
|
|
121
|
+
|
|
122
|
+
**Impact:** noise can obscure real overrides and accidentally pin behavior across SDK changes.
|
|
123
|
+
|
|
124
|
+
```xml
|
|
125
|
+
<!-- Potentially redundant for a library named MyLib with these SDK defaults. -->
|
|
126
|
+
<PropertyGroup>
|
|
127
|
+
<OutputType>Library</OutputType>
|
|
128
|
+
<EnableDefaultItems>true</EnableDefaultItems>
|
|
129
|
+
<EnableDefaultCompileItems>true</EnableDefaultCompileItems>
|
|
130
|
+
<RootNamespace>MyLib</RootNamespace>
|
|
131
|
+
<AssemblyName>MyLib</AssemblyName>
|
|
132
|
+
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
|
|
133
|
+
</PropertyGroup>
|
|
134
|
+
|
|
135
|
+
<!-- Keep the settings that actually express the project's contract. -->
|
|
136
|
+
<PropertyGroup>
|
|
137
|
+
<TargetFramework>net8.0</TargetFramework>
|
|
138
|
+
</PropertyGroup>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Verify the actual SDK/imports, configurations, naming, and intentional pins first. A setting that
|
|
142
|
+
matches today's default can still deliberately preserve behavior; do not remove it blindly.
|
|
143
|
+
|
|
144
|
+
## AP-05: Redundant file lists in SDK-style projects
|
|
145
|
+
|
|
146
|
+
**Smell:** explicit C#/VB source includes duplicate enabled SDK default items.
|
|
147
|
+
|
|
148
|
+
**Impact:** duplicate items, merge noise, and source additions that can be missed when defaults
|
|
149
|
+
have instead been disabled.
|
|
150
|
+
|
|
151
|
+
```xml
|
|
152
|
+
<!-- BAD when default Compile items already contain these files. -->
|
|
153
|
+
<ItemGroup>
|
|
154
|
+
<Compile Include="Program.cs" />
|
|
155
|
+
<Compile Include="Services\MyService.cs" />
|
|
156
|
+
</ItemGroup>
|
|
157
|
+
|
|
158
|
+
<!-- GOOD: keep only intentional exclusions or metadata. -->
|
|
159
|
+
<ItemGroup>
|
|
160
|
+
<Compile Remove="LegacyCode\**" />
|
|
161
|
+
<Compile Update="Services\MyService.cs" Visible="false" />
|
|
162
|
+
</ItemGroup>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Exceptions:** legacy projects, projects with default items intentionally disabled, linked files
|
|
166
|
+
outside default globs, generated files, and items with special metadata need their actual contract
|
|
167
|
+
preserved. `Update` modifies an existing item; it does not add a missing one.
|
|
168
|
+
|
|
169
|
+
**F# is order-dependent.** Keep explicit `.fsproj` compile items in dependency order. An `.fsi`
|
|
170
|
+
signature must immediately precede its matching `.fs`; the entry point is last. Do not apply C#
|
|
171
|
+
implicit-glob cleanup to F#.
|
|
172
|
+
|
|
173
|
+
## AP-06: Package DLL HintPaths instead of package references
|
|
174
|
+
|
|
175
|
+
**Smell:** package assemblies referenced through a legacy `packages` directory.
|
|
176
|
+
|
|
177
|
+
**Impact:** the project depends on a fixed restored layout instead of PackageReference asset
|
|
178
|
+
selection. This is a migration candidate, not proof that existing packages.config restore is broken.
|
|
179
|
+
|
|
180
|
+
```xml
|
|
181
|
+
<!-- Legacy package reference. -->
|
|
182
|
+
<Reference Include="Newtonsoft.Json">
|
|
183
|
+
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\netstandard2.0\Newtonsoft.Json.dll</HintPath>
|
|
184
|
+
</Reference>
|
|
185
|
+
|
|
186
|
+
<!-- PackageReference replacement, when migration is authorized and compatible. -->
|
|
187
|
+
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Preserve package versions, conditions, content/build assets, and restore behavior. Do not remove
|
|
191
|
+
GAC, local, or vendor assembly references just because they use `Reference` or `HintPath`.
|
|
192
|
+
|
|
193
|
+
## AP-07: Build-only packages exposed to consumers
|
|
194
|
+
|
|
195
|
+
**Smell:** a local analyzer/tool becomes an unwanted dependency or build asset of consumers.
|
|
196
|
+
|
|
197
|
+
**Impact:** consumers can receive implementation-only dependencies or transitive build behavior.
|
|
198
|
+
Inspect effective metadata and package assets; absence of a literal `PrivateAssets` attribute
|
|
199
|
+
does not prove all analyzer assets propagate.
|
|
200
|
+
|
|
201
|
+
Use `PrivateAssets="all"` for dependencies intended to remain entirely private. Preserve
|
|
202
|
+
intentionally public/transitive build contracts. See
|
|
203
|
+
[private-assets.md](antipatterns/private-assets.md) for examples and exceptions.
|
|
204
|
+
|
|
205
|
+
## AP-08: Copy-pasted shared properties
|
|
206
|
+
|
|
207
|
+
**Smell:** the same property block is repeated across several projects.
|
|
208
|
+
|
|
209
|
+
**Impact:** inconsistent updates and unclear ownership.
|
|
210
|
+
|
|
211
|
+
```xml
|
|
212
|
+
<!-- Directory.Build.props at the intended shared scope. -->
|
|
213
|
+
<Project>
|
|
214
|
+
<PropertyGroup>
|
|
215
|
+
<Nullable>enable</Nullable>
|
|
216
|
+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
|
217
|
+
<ImplicitUsings>enable</ImplicitUsings>
|
|
218
|
+
</PropertyGroup>
|
|
219
|
+
</Project>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Move only genuinely shared values. Preserve project overrides and property evaluation timing.
|
|
223
|
+
Only the nearest Directory.Build file is automatically discovered; see
|
|
224
|
+
[extension-points](extension-points.md#directorybuild-discovery) before changing hierarchy.
|
|
225
|
+
|
|
226
|
+
## AP-09: Unintended package-version drift
|
|
227
|
+
|
|
228
|
+
**Smell:** projects unintentionally use different versions of the same package.
|
|
229
|
+
|
|
230
|
+
**Impact:** divergent behavior, conflicts, and difficult dependency maintenance.
|
|
231
|
+
|
|
232
|
+
```xml
|
|
233
|
+
<!-- ProjectA.csproj -->
|
|
234
|
+
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
|
235
|
+
<!-- ProjectB.csproj -->
|
|
236
|
+
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Central Package Management can consolidate versions when that migration is requested; see
|
|
240
|
+
[the NuGet guidance](https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management).
|
|
241
|
+
Keep intentional framework/project differences. Do not silently upgrade packages or centralize
|
|
242
|
+
the whole repository while fixing an unrelated build.
|
|
243
|
+
|
|
244
|
+
## AP-10: Monolithic targets
|
|
245
|
+
|
|
246
|
+
**Smell:** one target mixes generation, copying, signing, and other unrelated operations.
|
|
247
|
+
|
|
248
|
+
**Impact:** the steps cannot be scheduled, diagnosed, or skipped independently.
|
|
249
|
+
|
|
250
|
+
```xml
|
|
251
|
+
<!-- BAD: unrelated work in one target. -->
|
|
252
|
+
<Target Name="PrepareRelease" BeforeTargets="Build">
|
|
253
|
+
<WriteLinesToFile File="version.txt" Lines="$(Version)" Overwrite="true" />
|
|
254
|
+
<Copy SourceFiles="LICENSE" DestinationFolder="$(OutputPath)" />
|
|
255
|
+
<Exec Command="signtool sign /f cert.pfx $(OutputPath)*.dll" />
|
|
256
|
+
</Target>
|
|
257
|
+
|
|
258
|
+
<!-- GOOD: separate responsibilities with their own contracts. -->
|
|
259
|
+
<Target Name="WriteVersionFile" BeforeTargets="CoreCompile">
|
|
260
|
+
<MakeDir Directories="$(IntermediateOutputPath)" />
|
|
261
|
+
<WriteLinesToFile File="$(IntermediateOutputPath)version.txt"
|
|
262
|
+
Lines="$(Version)" Overwrite="true" WriteOnlyWhenDifferent="true" />
|
|
263
|
+
<ItemGroup>
|
|
264
|
+
<FileWrites Include="$(IntermediateOutputPath)version.txt" />
|
|
265
|
+
</ItemGroup>
|
|
266
|
+
</Target>
|
|
267
|
+
<Target Name="CopyLicense" AfterTargets="CopyFilesToOutputDirectory">
|
|
268
|
+
<Copy SourceFiles="LICENSE" DestinationFolder="$(OutputPath)" SkipUnchangedFiles="true">
|
|
269
|
+
<Output TaskParameter="CopiedFiles" ItemName="FileWrites" />
|
|
270
|
+
</Copy>
|
|
271
|
+
</Target>
|
|
272
|
+
<Target Name="SignAssemblies" AfterTargets="Build" DependsOnTargets="CopyLicense"
|
|
273
|
+
Condition="'$(SignAssemblies)' == 'true'">
|
|
274
|
+
<ItemGroup>
|
|
275
|
+
<AssemblyFiles Include="$(OutputPath)*.dll" />
|
|
276
|
+
</ItemGroup>
|
|
277
|
+
<Error Condition="'@(AssemblyFiles)' == ''"
|
|
278
|
+
Text="Signing was requested, but no assemblies were found in '$(OutputPath)'." />
|
|
279
|
+
<Exec Command="signtool sign /f cert.pfx "%(AssemblyFiles.Identity)"" />
|
|
280
|
+
</Target>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
The cheap version writer intentionally checks the value each build: `Version` can change through
|
|
284
|
+
global properties without any project-file timestamp changing. A project-file-only `Inputs`
|
|
285
|
+
declaration would incorrectly reuse stale content. For expensive generation, model value changes
|
|
286
|
+
with a stable input fingerprint as in [AP-11's example](antipatterns/incremental-build-inputs-outputs.md).
|
|
287
|
+
|
|
288
|
+
Preserve original target order, supported platforms, required tools, and copy semantics.
|
|
289
|
+
`SkipUnchangedFiles` is appropriate only when its timestamp/size heuristic satisfies the contract.
|
|
290
|
+
Register the copied destinations in `FileWrites` before MSBuild records them for a subsequent
|
|
291
|
+
`Clean`. Hooking `CopyLicense` after `CopyFilesToOutputDirectory` does this even for unchanged
|
|
292
|
+
copies; registering them in an `AfterTargets="Build"` target would be too late.
|
|
293
|
+
Discover the assemblies inside the signing target, after the build has produced them, rather
|
|
294
|
+
than with an evaluation-time glob. This retains the original output-directory DLL selection.
|
|
295
|
+
|
|
296
|
+
## AP-11: Untracked artifact-producing targets
|
|
297
|
+
|
|
298
|
+
**Smell:** expensive file generation runs on every no-change build because its inputs/outputs are
|
|
299
|
+
not modeled, or skips despite a relevant change.
|
|
300
|
+
|
|
301
|
+
**Impact:** repeated work or stale outputs. Inputs must cover actual files and relevant property,
|
|
302
|
+
configuration, generator, and import changes.
|
|
303
|
+
|
|
304
|
+
See [incremental-build-inputs-outputs.md](antipatterns/incremental-build-inputs-outputs.md) for a
|
|
305
|
+
correctness-oriented example, and [incremental-build](incremental-build.md) for full diagnosis.
|
|
306
|
+
Cheap validation, orchestration, and item-discovery targets may legitimately run without
|
|
307
|
+
file-based outputs; do not invent a stamp to suppress necessary work.
|
|
308
|
+
|
|
309
|
+
## AP-12: Defaults defined after their consumers
|
|
310
|
+
|
|
311
|
+
**Smell:** a default is set in a late import after another file already used it.
|
|
312
|
+
|
|
313
|
+
**Impact:** consumers observe an empty or unintended earlier value.
|
|
314
|
+
|
|
315
|
+
```xml
|
|
316
|
+
<!-- custom.props: an early, overridable default. -->
|
|
317
|
+
<PropertyGroup>
|
|
318
|
+
<MyToolVersion Condition="'$(MyToolVersion)' == ''">2.0</MyToolVersion>
|
|
319
|
+
</PropertyGroup>
|
|
320
|
+
|
|
321
|
+
<!-- custom.targets: execution using that setting. -->
|
|
322
|
+
<Target Name="RunMyTool">
|
|
323
|
+
<Exec Command="mytool --version $(MyToolVersion)" />
|
|
324
|
+
</Target>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
`.props` normally carries early defaults; `.targets` normally carries later build logic.
|
|
328
|
+
Timing matters more than the suffix: a value depending on a project-defined property must not
|
|
329
|
+
move earlier than that property exists. See AP-21 in
|
|
330
|
+
[additional anti-patterns](antipatterns/additional-antipatterns.md).
|
|
331
|
+
|
|
332
|
+
## AP-13: Missing guards on optional imports
|
|
333
|
+
|
|
334
|
+
**Smell:** an optional developer/environment override is imported unconditionally.
|
|
335
|
+
|
|
336
|
+
**Impact:** a fresh checkout fails because a file that is optional by contract is absent.
|
|
337
|
+
|
|
338
|
+
```xml
|
|
339
|
+
<!-- Guard an optional override. -->
|
|
340
|
+
<Import Project="$(RepoRoot)eng\local.props"
|
|
341
|
+
Condition="Exists('$(RepoRoot)eng\local.props')" />
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Required imports must fail when missing.** Do not add an existence guard to hide a missing SDK,
|
|
345
|
+
required build file, or broken package contract.
|
|
346
|
+
|
|
347
|
+
**NuGet forwarders need a packed-layout check.** Files in `build`/`buildTransitive` can import
|
|
348
|
+
paths that do not exist in source but are created by `.nuspec` mappings, `PackagePath` metadata,
|
|
349
|
+
or SDK pack conventions. First inspect the effective `NuspecFile`, `NuspecBasePath`, and
|
|
350
|
+
`NuspecProperties`, including imports and global-property overrides. Follow the configured
|
|
351
|
+
nuspec even outside the project tree and resolve its mappings with those settings. Only when
|
|
352
|
+
no custom nuspec is configured, inspect nearby `.nuspec` files in the project and its immediate
|
|
353
|
+
parent as a bounded fallback. Inspect the project's packaging items too; do not search unrelated
|
|
354
|
+
ancestors or treat an unavailable configured nuspec as proof of a broken import.
|
|
355
|
+
|
|
356
|
+
Only flag the target if it is absent from both applicable source and projected package layouts.
|
|
357
|
+
See [source tree vs packed layout](extension-points.md#source-tree-vs-packed-layout).
|
|
358
|
+
For per-TFM forwarders, derive the matched folder from the file's own location rather than the
|
|
359
|
+
consumer's `TargetFramework`; see the [forwarding chain](extension-points.md#forwarding-chain).
|
|
360
|
+
|
|
361
|
+
## AP-14: Backslashes where path normalization does not apply
|
|
362
|
+
|
|
363
|
+
**Smell:** Windows separators in a path intended to reach a non-MSBuild consumer on another OS.
|
|
364
|
+
|
|
365
|
+
**Correctness issue:** raw shell strings, generated source/response files, or custom tasks that
|
|
366
|
+
use OS APIs directly may interpret backslashes differently.
|
|
367
|
+
|
|
368
|
+
**Not automatically a correctness issue:** `Import` paths, item globs, and file paths consumed by
|
|
369
|
+
built-in tasks use MSBuild path handling. An ordinary backslash-style import is not, by itself,
|
|
370
|
+
a Linux failure.
|
|
371
|
+
|
|
372
|
+
```xml
|
|
373
|
+
<!-- A raw Windows shell/tool path needs actual platform support and quoting. -->
|
|
374
|
+
<Exec Command="$(MSBuildThisFileDirectory)tools\release\sign.exe $(OutputPath)" />
|
|
375
|
+
|
|
376
|
+
<!-- MSBuild handles this import path. Do not report a defect solely for its separators. -->
|
|
377
|
+
<Import Project="$(MSBuildThisFileDirectory)..\..\build\common.props" />
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Trace the consumer before assigning severity. `MSBuildThisFileDirectory` already ends with a
|
|
381
|
+
separator; use path functions when constructing paths rather than concatenating arbitrary strings.
|
|
382
|
+
|
|
383
|
+
## AP-15: Unexpected property overrides across scopes
|
|
384
|
+
|
|
385
|
+
**Smell:** a shared value is unexpectedly replaced by another unconditional assignment.
|
|
386
|
+
|
|
387
|
+
**Impact:** last-write behavior is difficult to explain and can alter output paths or options.
|
|
388
|
+
|
|
389
|
+
```xml
|
|
390
|
+
<!-- Directory.Build.props: a default that permits an existing value. -->
|
|
391
|
+
<PropertyGroup>
|
|
392
|
+
<OutputPath Condition="'$(OutputPath)' == ''">bin\custom\</OutputPath>
|
|
393
|
+
</PropertyGroup>
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Trace assignments and consumers first. A project overriding a repository default can be
|
|
397
|
+
intentional, and command-line global properties have different override semantics. Do not add
|
|
398
|
+
conditions everywhere or change path-defining properties too late for their SDK/NuGet consumers.
|
|
399
|
+
|
|
400
|
+
For AP-16 through AP-23 and the quick-reference checklist, read
|
|
401
|
+
[additional-antipatterns.md](antipatterns/additional-antipatterns.md).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Managed Code
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Microsoft Orleans
|
|
2
|
+
Copyright (c) 2026 Managed Code
|
|
3
|
+
Licensed under the MIT License. See LICENSE.
|
|
4
|
+
|
|
5
|
+
Upstream source: https://github.com/managedcode/dotnet-skills/blob/2d8fba176a33b9872d5de35ec71d9ec71112ee26/catalog/Frameworks/Orleans/skills/orleans/SKILL.md
|
|
6
|
+
|
|
7
|
+
Changes made by Cratis:
|
|
8
|
+
|
|
9
|
+
LICENSE:
|
|
10
|
+
- Copy upstream MIT license and copyright verbatim.
|
|
11
|
+
|
|
12
|
+
SKILL.md:
|
|
13
|
+
- Precedence line spells out the `[Alias]` scope from `.cratis/ai/rules/orleans.md` (grain interfaces and methods vs serializer records).
|
|
14
|
+
- Normalize frontmatter serialization, add MIT license where absent; preserve name and description.
|
|
15
|
+
- Add marked modification/source comment and applicability/Cratis precedence line with the skill-specific safety, ownership or compatibility caveat.
|
|
16
|
+
- Scope upstream general workflow advice to this skill's stated task; it does not govern unrelated tasks.
|
|
17
|
+
- Rewrite the description to the Cratis shape (what it does, Use when, Not for) and drop references to skills that are not part of this corpus.
|
|
18
|
+
|
|
19
|
+
references/grain-api.md:
|
|
20
|
+
- Qualify activation liveness for KeepAlive timers.
|
|
21
|
+
- Clarify the resource-optimized placement default from Orleans 9.2, random before.
|
|
22
|
+
- Add or normalize the top Contents list from existing headings; preserve section content.
|
|
23
|
+
|
|
24
|
+
references/implementation.md:
|
|
25
|
+
- Clarify the resource-optimized placement default from Orleans 9.2, random before.
|
|
26
|
+
- Add or normalize the top Contents list from existing headings; preserve section content.
|
|
27
|
+
|
|
28
|
+
references/patterns.md:
|
|
29
|
+
- Use the modern timer extension receiver and GrainTimerCreationOptions overload in the batching example.
|
|
30
|
+
- Correct the batching timer callback to Func<CancellationToken, Task>.
|
|
31
|
+
- Add or normalize the top Contents list from existing headings; preserve section content.
|
|
32
|
+
|
|
33
|
+
references/serialization-api.md:
|
|
34
|
+
- Correct the persistence serialization default to provider-specific, including Redis.
|
|
35
|
+
- Correct the default-serializer code comment to include Redis.
|
|
36
|
+
- Add or normalize the top Contents list from existing headings; preserve section content.
|
|
37
|
+
|
|
38
|
+
references/anti-patterns.md:
|
|
39
|
+
- Add or normalize the top Contents list from existing headings; preserve section content.
|
|
40
|
+
|
|
41
|
+
references/configuration-api.md:
|
|
42
|
+
- Add or normalize the top Contents list from existing headings; preserve section content.
|
|
43
|
+
|
|
44
|
+
references/official-docs-index.md:
|
|
45
|
+
- Remove the release-notes list; point links at the main branch and drop version labels.
|
|
46
|
+
- Add or normalize the top Contents list from existing headings; preserve section content.
|
|
47
|
+
|
|
48
|
+
references/persistence-api.md:
|
|
49
|
+
- Add or normalize the top Contents list from existing headings; preserve section content.
|
|
50
|
+
|
|
51
|
+
references/scheduling-and-services.md:
|
|
52
|
+
- Remove release-notes link; point links at the main branch; describe the prerelease suffix without a version.
|
|
53
|
+
- Add or normalize the top Contents list from existing headings; preserve section content.
|
|
54
|
+
|
|
55
|
+
references/streaming-api.md:
|
|
56
|
+
- Add or normalize the top Contents list from existing headings; preserve section content.
|
|
57
|
+
|
|
58
|
+
references/testing-patterns.md:
|
|
59
|
+
- Add or normalize the top Contents list from existing headings; preserve section content.
|
|
60
|
+
|
|
61
|
+
references/grains.md:
|
|
62
|
+
- Point links at the main branch and drop version labels.
|
|
63
|
+
|
|
64
|
+
NOTICE: Add this attribution, change list, and any download/execution disclosure.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: orleans
|
|
3
|
+
description: 'Designs Microsoft Orleans systems from each primitive''s purpose and failure model: grains, digital twins, state versus databases, transactions, messaging, streams, timers, reminders, Durable Jobs, stateless workers, grain services, startup, and hosting. Use when a task needs an Orleans design decision: inspect version and topology, choose the primitive, implement, and validate. Not for other actor stacks, batch processing, relational-only CRUD, or advice without an Orleans decision.'
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!-- Modified by Cratis from managedcode's original (MIT): frontmatter, applicability, navigation and the changes listed in NOTICE. Upstream: https://github.com/managedcode/dotnet-skills (catalog/Frameworks/Orleans/skills/orleans/SKILL.md; see NOTICE) -->
|
|
8
|
+
|
|
9
|
+
# Microsoft Orleans
|
|
10
|
+
|
|
11
|
+
Use only within this skill's stated task; general workflow advice does not apply to unrelated tasks. Cratis rules in `.cratis/ai/rules/` (including `csharp.md`, `specs.csharp.md`, and `orleans.md`) and the `cratis-*` skills win where they differ. `.cratis/ai/rules/orleans.md` forbids `[Alias]` on grain interfaces and grain methods (it is used only on `[GenerateSerializer]` state and definition records that must survive a rename) and forbids `ConfigureAwait(false)` in grain code; **cratis-orleans-jobs** owns recoverable jobs. DurableJobs/Journaling are experimental and are not interchangeable with Cratis.Orleans jobs.
|
|
12
|
+
|
|
13
|
+
## Diagnostic Output Budget
|
|
14
|
+
|
|
15
|
+
Keep native test progress and ANSI visible; use the detected runner's supported flags (`--progress on --ansi on` for MTP/TUnit), not MTP switches on VSTest. Keep console logs at `Warning` or higher and one concise final summary. Do not replay progress redraws, successful-test output, or Information/Debug/Trace logs into model context. On failure/crash, show only the failing test/resource, root error, and relevant stack frames; deduplicate and cap each diagnostic response at 80 lines / 8 KiB. Never dump entire console/host/browser logs, HTML, TRX, or crash artifacts. Keep necessary artifacts size-bounded outside context, link them, and inspect exact bounded excerpts. Preserve the runner exit code through capture/filtering; disclose truncation. Silence alone does not establish a hang.
|
|
16
|
+
|
|
17
|
+
## Start With Purpose
|
|
18
|
+
|
|
19
|
+
Do not begin with an Orleans API. First state:
|
|
20
|
+
|
|
21
|
+
1. the business identity that owns the behavior;
|
|
22
|
+
2. the invariant and what must survive activation or cluster failure;
|
|
23
|
+
3. the required consistency, query shape, acknowledgement, durability, replay, fan-out, and timing.
|
|
24
|
+
|
|
25
|
+
Then select the smallest Orleans primitive whose guarantees match those requirements. Reject Orleans when the problem is primarily shared-memory computation, a finite batch, relational querying, or global coordination with few independent entities.
|
|
26
|
+
|
|
27
|
+
Inspect package versions for version-sensitive work. Orleans `10.3.1` ships `Microsoft.Orleans.DurableJobs*` and `Microsoft.Orleans.Journaling*` as `10.3.1-alpha.1`; treat them as experimental until that status changes.
|
|
28
|
+
|
|
29
|
+
Orleans 10.3 makes Newtonsoft storage enforce the type allow-list, changes RPC telemetry keys, and requires custom grain-context activators to apply configurators before construction. Prefer generated/allowed types over permissive JSON and update affected tests, dashboards, and activators together. `10.3.1` services analyzer contract identities and documents placement hints.
|
|
30
|
+
|
|
31
|
+
## Mental Model
|
|
32
|
+
|
|
33
|
+
- A **grain** is a virtual actor with stable identity, behavior, and optional state, not a process, row, DTO, controller, or background job.
|
|
34
|
+
- A **grain reference** is a location-transparent address. Getting a reference does not create a durable record or prove that an activation exists.
|
|
35
|
+
- An **activation** is an ephemeral in-memory execution instance. Orleans creates, places, moves, deactivates, and recreates it. Never equate activation lifetime with entity lifetime.
|
|
36
|
+
- A normal grain has at most one activation in the cluster by default and processes turns one at a time. This makes the grain a natural owner of per-identity invariants.
|
|
37
|
+
- A **silo** hosts activations. Silos form a cluster; external clients or a co-hosted `IGrainFactory` call grains.
|
|
38
|
+
- Calls are asynchronous messages even when they look like C# method calls. Network failure, timeout, serialization, retries, and duplicate side effects still matter.
|
|
39
|
+
- A grain can model a **digital twin** when its identity and behavior map to a real or domain entity; that is a use case, not every grain's definition.
|
|
40
|
+
- Orleans gives logical ownership and turn-based execution. It does not make external side effects transactional, turn arbitrary data into a queryable database, or provide exactly-once execution by default.
|
|
41
|
+
|
|
42
|
+
## Workflow
|
|
43
|
+
|
|
44
|
+
1. Inspect the solution, Orleans version, hosting topology, grain interfaces, providers, and tests.
|
|
45
|
+
2. Identify domain identities and invariants. Prefer many independent, bounded entities over global coordinator grains.
|
|
46
|
+
3. Choose state, communication, and time-based work from the tables below.
|
|
47
|
+
4. Keep default non-reentrant scheduling and placement until a measured requirement justifies a change.
|
|
48
|
+
5. Configure providers independently and test the failure model the design depends on.
|
|
49
|
+
|
|
50
|
+
## Choose the State Owner
|
|
51
|
+
|
|
52
|
+
| Primitive | Purpose | Choose it when | Do not use it as |
|
|
53
|
+
|---|---|---|---|
|
|
54
|
+
| Activation fields | Fast, temporary state for one activation | The value is derived, cached, disposable, or safe to rebuild after deactivation/failure | Durable truth |
|
|
55
|
+
| `IPersistentState<T>` | Durable current state owned by one grain identity | The grain needs a bounded snapshot loaded on activation and explicitly written after commands | A general query database or cross-grain table |
|
|
56
|
+
| External database/repository | Queryable, indexed, relational, bulk, shared, or externally owned data | The system needs joins, search, reporting, set-based updates, independent access, or an existing system of record | A replacement for grain ownership when serialized per-entity decisions are still required |
|
|
57
|
+
| Grain plus database/read model | Separate command ownership from query/storage concerns | A grain owns invariants and a small control snapshot while a database owns large records, history, projections, or reporting | Two competing sources of truth without an explicit contract |
|
|
58
|
+
| `JournaledGrain<TState,TEvent>` | Persist domain events and reconstruct state | Audit history, business-event replay, log consistency, or multi-cluster event-sourced replication is a requirement | A default persistence choice for ordinary CRUD state |
|
|
59
|
+
| `Orleans.Journaling` durable states | Replay durable collection/value operations through a journal | The experimental 10.3 journaling model, durable collections, or durable completion state solves a measured need | Stable default persistence; it is distinct from `JournaledGrain` business event sourcing |
|
|
60
|
+
| `ITransactionalState<T>` | ACID, serializable all-or-nothing changes across transactional grain state | A short operation must atomically update multiple grain-owned states and compensation is unacceptable | Long-running workflows or atomicity with arbitrary external systems |
|
|
61
|
+
| Saga/process manager | Durable progress with compensation across steps and external systems | Work is long-running, spans services, waits for events, or cannot share one transaction | Instant atomic commit |
|
|
62
|
+
|
|
63
|
+
### Grain State Versus a Database
|
|
64
|
+
|
|
65
|
+
Use grain state for bounded current state and per-identity invariants. Use a database/read model for joins, search, reporting, bulk work, history, shared access, or an external system of record. When using both, define one authority per field and recovery rules.
|
|
66
|
+
|
|
67
|
+
Never query or mutate another grain's persistence record behind the grain, or expose provider storage as the public query model merely because it uses SQL/Cosmos/Redis. Before using transactions, try one bounded grain owner; use a saga for external effects or long waits. Read [references/persistence-api.md](references/persistence-api.md) for the full boundary.
|
|
68
|
+
|
|
69
|
+
## Choose Communication
|
|
70
|
+
|
|
71
|
+
Prefer a direct call for completion to a known owner. Use streams for decoupled provider-backed delivery, response streams for one caller, and broadcast/observers/one-way calls only for loss-tolerant notifications. A timeout does not prove whether an external effect committed; retries require idempotency. Read [references/primitive-selection.md](references/primitive-selection.md) for the communication guarantees and execution choices.
|
|
72
|
+
|
|
73
|
+
## Choose Time-Based and Background Work
|
|
74
|
+
|
|
75
|
+
| Primitive | Purpose | Choose it when | Do not choose it when |
|
|
76
|
+
|---|---|---|---|
|
|
77
|
+
| `RegisterGrainTimer` | Periodic or one-shot work tied to the current activation | Work is frequent, local to an active grain, and safe to stop on deactivation or silo failure | The schedule must survive reactivation/restart |
|
|
78
|
+
| Reminder | Durable recurring schedule definition associated with a grain identity | Low-frequency recurring work must wake the grain after deactivation or cluster restart | Every missed occurrence must be replayed, timing must be precise, or work is high-frequency |
|
|
79
|
+
| Durable Job | Persistent one-time future delivery to a target grain with cancellation/retry metadata | A delayed command, expiry, notification, or workflow step must execute at least once around a due time | Recurring work, exactly-once side effects, or production work that cannot accept the current alpha package status |
|
|
80
|
+
| `[StatelessWorker]` | Auto-scaled pool of local stateless grain activations | CPU/transform/routing/pre-aggregation work is not tied to one durable entity | Scheduling or durability; a stateless worker is not a job system |
|
|
81
|
+
| `BackgroundService` | Continuous loop owned by each host process | A silo/web host must poll or consume an external source and forward work into grains | One global loop across replicas unless duplicates are safe or externally coordinated |
|
|
82
|
+
| `IHostedService` | Host startup/shutdown action or simpler background component | Initialization or bounded host-lifetime work belongs to standard .NET hosting | Per-entity durable work |
|
|
83
|
+
| Orleans startup task | Fail-fast hook at a specific silo startup stage | Legacy/framework integration truly requires Orleans lifecycle ordering | General background work; prefer `BackgroundService` or `IHostedService` |
|
|
84
|
+
| Silo lifecycle participant | Ordered initialization/shutdown of an Orleans component | A provider or runtime service must start at an exact lifecycle stage | Business scheduling |
|
|
85
|
+
| Grain service | Per-silo, cluster-partitioned runtime support service | Every silo hosts a long-lived service and responsibility for grains must be partitioned across silos | An ordinary domain entity, a singleton, or a durable job queue |
|
|
86
|
+
| External scheduler/workflow engine | Scheduling/orchestration outside Orleans | Cross-system workflows, cron calendars, human steps, broad operational control, or mature production guarantees dominate | Per-grain work already solved by a stable Orleans primitive |
|
|
87
|
+
|
|
88
|
+
### Timer, Reminder, or Durable Job
|
|
89
|
+
|
|
90
|
+
Timer means “while this activation lives”; reminder means “wake this grain on a durable recurring schedule”; Durable Job means “invoke this target once around a future time, at least once.” Reminders persist definitions but miss ticks while the cluster is down. Durable Job handlers must be idempotent, and current `alpha.1` packages are an explicit architecture risk.
|
|
91
|
+
|
|
92
|
+
`BackgroundService` means one loop per host replica, not one loop per cluster. Use a well-known grain or external lease/leader for one logical collector.
|
|
93
|
+
|
|
94
|
+
Read [references/scheduling-and-services.md](references/scheduling-and-services.md) before implementing timers, reminders, Durable Jobs, hosted/startup tasks, silo lifecycle participants, or grain services.
|
|
95
|
+
|
|
96
|
+
## Choose Execution and Scaling
|
|
97
|
+
|
|
98
|
+
Use standard grains for stateful identities and stateless workers for fungible work. Apply narrow interleaving attributes only after auditing invariants across every `await`; grain turns do not make reentrant workflows race-free. Preserve default placement unless a measured locality or compatibility requirement justifies changing it. See [references/primitive-selection.md](references/primitive-selection.md) before changing scheduling or placement.
|
|
99
|
+
|
|
100
|
+
## Hosting and Provider Boundaries
|
|
101
|
+
|
|
102
|
+
Keep concerns separate: clustering discovers silos; the grain directory locates activations; grain/reminder/transaction/job storage persist different records; stream providers carry events while `PubSubStore` tracks subscriptions; serialization defines wire and persistence compatibility.
|
|
103
|
+
|
|
104
|
+
In-memory clustering, storage, reminders, streams, Durable Jobs, and journaling are development/test choices unless loss is explicitly acceptable. Configure production providers, credentials, TLS/networking, server GC, graceful shutdown, and health/readiness for the deployment target. In Aspire, declare backing resources in AppHost and register the keyed clients expected by Orleans providers.
|
|
105
|
+
|
|
106
|
+
## Contract and API Rules
|
|
107
|
+
|
|
108
|
+
- Keep grain interfaces coarse-grained and asynchronous: `Task`, `Task<T>`, `ValueTask<T>`, or supported `IAsyncEnumerable<T>`.
|
|
109
|
+
- Cancellation is cooperative, not proof that work did not finish.
|
|
110
|
+
- Use `[GenerateSerializer]` and stable `[Id(N)]` values on messages/state. Use `[Alias]` for durable type identity and `[Immutable]` only for genuinely immutable values.
|
|
111
|
+
- Never reuse removed field IDs; write state explicitly and propagate storage failures.
|
|
112
|
+
- Bound external calls and use idempotency plus a saga/outbox when state writes and external effects must be reconciled.
|
|
113
|
+
|
|
114
|
+
## Validate the Chosen Guarantees
|
|
115
|
+
|
|
116
|
+
- Grain boundaries follow business identity and avoid hot global coordinators.
|
|
117
|
+
- State is bounded, and the grain-state-versus-database choice is explicit.
|
|
118
|
+
- Every time primitive matches activation, recurrence, durability, and delivery requirements.
|
|
119
|
+
- Durable Job handlers/retried commands are idempotent; reminders tolerate missed ticks.
|
|
120
|
+
- Broadcast channels, observers, and one-way calls carry only loss-tolerant signals.
|
|
121
|
+
- Provider-backed tests prove delivery, ordering, replay, and failover claims.
|
|
122
|
+
- Reentrancy tests cover state across `await`; transactions use transactional storage, `[Reentrant]`, and `PerformRead`/`PerformUpdate`.
|
|
123
|
+
- Hosted services are reviewed for per-replica duplication.
|
|
124
|
+
- Production uses persistent providers, multi-silo tests, and observability wherever the guarantee depends on them.
|
|
125
|
+
|
|
126
|
+
## Load References
|
|
127
|
+
|
|
128
|
+
Open only the references needed for the selected primitive:
|
|
129
|
+
|
|
130
|
+
- [references/official-docs-index.md](references/official-docs-index.md) — official documentation map
|
|
131
|
+
- [references/scheduling-and-services.md](references/scheduling-and-services.md) — scheduled/background/runtime work
|
|
132
|
+
- [references/grains.md](references/grains.md) and [references/grain-api.md](references/grain-api.md) — grain design and APIs
|
|
133
|
+
- [references/persistence-api.md](references/persistence-api.md) — state, databases, Journaling, event sourcing, transactions
|
|
134
|
+
- [references/streaming-api.md](references/streaming-api.md) — streams, broadcast, observers, response streams
|
|
135
|
+
- [references/serialization-api.md](references/serialization-api.md) — serialization and versioning
|
|
136
|
+
- [references/hosting.md](references/hosting.md) and [references/configuration-api.md](references/configuration-api.md) — hosting, providers, operations
|
|
137
|
+
- [references/implementation.md](references/implementation.md) and [references/testing-patterns.md](references/testing-patterns.md) — runtime and tests
|
|
138
|
+
- [references/patterns.md](references/patterns.md), [references/anti-patterns.md](references/anti-patterns.md), and [references/examples.md](references/examples.md) — patterns and samples
|
|
139
|
+
|
|
140
|
+
Prefer Learn for stable APIs. For Durable Jobs and `Orleans.Journaling`, use version-tagged package READMEs/public API because Learn does not yet cover them fully.
|