@cratis/pi 7.113.0 → 7.114.1
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/orleans.instructions.md +10 -2
- package/package/corpus/harnesses/copilot/instructions/vertical-slices.instructions.md +1 -1
- package/package/corpus/hooks/scripts/cratis-store-mutations.json +4 -0
- package/package/corpus/product-sync.json +12 -12
- package/package/corpus/rules/orleans.md +10 -2
- package/package/corpus/rules/vertical-slices.md +1 -1
- package/package/corpus/skills/cratis-application-slice-specifications/references/query-and-decision-scenarios.md +19 -0
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +8 -0
- package/package/corpus/skills/cratis-arc-command/references/proxy-generation.md +10 -1
- package/package/corpus/skills/cratis-arc-command-execution/SKILL.md +3 -2
- package/package/corpus/skills/cratis-arc-command-execution/references/occurrence-commands.md +7 -6
- package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +5 -0
- package/package/corpus/skills/cratis-arc-command-validation/references/input-binding.md +17 -0
- package/package/corpus/skills/cratis-arc-command-validation/references/protected-validator-construction.md +94 -0
- package/package/corpus/skills/cratis-arc-ef-core-migration/SKILL.md +18 -0
- package/package/corpus/skills/cratis-arc-query-paging/SKILL.md +11 -4
- package/package/corpus/skills/cratis-arc-query-paging/references/live-query-processing.md +71 -0
- package/package/corpus/skills/cratis-chronicle-cli-operations/SKILL.md +11 -2
- package/package/corpus/skills/cratis-chronicle-cli-operations/references/command-effects.md +7 -0
- package/package/corpus/skills/cratis-chronicle-cli-operations/references/project-tooling.md +91 -0
- package/package/corpus/skills/cratis-chronicle-cli-operations/references/recovery-and-comparison.md +68 -0
- package/package/corpus/skills/cratis-chronicle-read-model/references/queries.md +5 -3
- package/package/corpus/skills/cratis-cli-terminal-workbench/SKILL.md +5 -1
- package/package/corpus/skills/cratis-orleans-jobs/SKILL.md +39 -51
- package/package/corpus/skills/cratis-orleans-jobs/references/lifecycle-and-placement.md +52 -0
- package/package/corpus/skills/cratis-orleans-jobs/references/state-machines.md +3 -1
- package/package.json +1 -1
|
@@ -11,7 +11,7 @@ paths:
|
|
|
11
11
|
> **⚠️ APPLIES ONLY TO PROJECTS USING MICROSOFT ORLEANS**
|
|
12
12
|
> If your project does not reference `Microsoft.Orleans` or any Orleans packages, **ignore this entire file**. These rules are irrelevant outside of Orleans contexts.
|
|
13
13
|
|
|
14
|
-
In the Cratis organization, Orleans is the Chronicle kernel's actor runtime (`Source/Kernel/`). The kernel conventions below are verified against `Cratis/Chronicle` **v19.50.0**. Shared hosting and jobs come from `Cratis/Orleans` **v1.
|
|
14
|
+
In the Cratis organization, Orleans is the Chronicle kernel's actor runtime (`Source/Kernel/`). The kernel conventions below are verified against `Cratis/Chronicle` **v19.50.0**. Shared hosting and jobs come from `Cratis/Orleans` **v1.30.0**; use its setup for other services that co-host a silo.
|
|
15
15
|
|
|
16
16
|
## General
|
|
17
17
|
|
|
@@ -50,7 +50,7 @@ framework/service guidance; they do not impose application slice architecture.
|
|
|
50
50
|
|
|
51
51
|
For service authors, `Cratis.Orleans` supplies hosting, grain serializers, and the
|
|
52
52
|
shared job system without requiring Chronicle. With `Cratis.Orleans` and
|
|
53
|
-
`Cratis.Orleans.Storage.MongoDB` 1.
|
|
53
|
+
`Cratis.Orleans.Storage.MongoDB` 1.30.0, this host uses durable MongoDB membership
|
|
54
54
|
and jobs storage (a local MongoDB server is required):
|
|
55
55
|
|
|
56
56
|
```csharp
|
|
@@ -81,6 +81,14 @@ app.Run();
|
|
|
81
81
|
- Register jobs storage separately: `AddCratisOrleansMongoDBJobsStorage`,
|
|
82
82
|
`AddCratisOrleansSqlJobsStorage` (`Cratis.Orleans.Setup`), or
|
|
83
83
|
`AddCratisOrleansInMemoryJobsStorage` (`Cratis.Orleans.Storage`).
|
|
84
|
+
- Use the local `IJobs` service to start/stop/delete typed jobs during rolling
|
|
85
|
+
upgrades; generic manager calls can land on a release that does not know the
|
|
86
|
+
type. Provide singleton `IJobsManagerKeys` on every silo for all host-owned
|
|
87
|
+
scope/namespace pairs; process-local discovery is not a recovery catalog.
|
|
88
|
+
- Controller and worker placement are separate opt-ins through `AddJobPlacement`
|
|
89
|
+
and `AddJobStepPlacement`. Enable them only after every silo supports the
|
|
90
|
+
strategies and register them identically. Worker throttling is not admission;
|
|
91
|
+
see **cratis-orleans-jobs** for drain-before-replacement and metadata limits.
|
|
84
92
|
- Give grain implementations a unique `[GrainType("unique-name")]`. Orleans'
|
|
85
93
|
default type name derives from the class name alone; a collision across
|
|
86
94
|
assemblies prevents silo startup.
|
|
@@ -258,7 +258,7 @@ public record AuthorRegistered(AuthorName Name);
|
|
|
258
258
|
|
|
259
259
|
## Read Models and Queries
|
|
260
260
|
|
|
261
|
-
**[contract]** `[ReadModel]` records expose eligible public
|
|
261
|
+
**[contract]** `[ReadModel]` records expose eligible public static methods as queries. Requires Arc 22.121.0 for non-public methods to be excluded even with `[Query]` (ARC0029 warns); older versions also expose eligible internal statics. Keep helpers outside the read model when supporting those versions. Dependencies are plain method parameters (no `[FromServices]`). For application code, start with model-bound mappings, use fluent when the definition needs structure, and choose reducer code when transitions read better as code. For step-by-step creation, invoke the **cratis-chronicle-read-model** skill.
|
|
262
262
|
|
|
263
263
|
Use `[ReadModel(RequireQueryAttribute = true)]` with `[Query]` on intended
|
|
264
264
|
endpoints when folds exist (ARC0026/ARCCHR0019). `[Query]` alone does not hide
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
{ "command": "read-models occurrences", "reason": "lists a read model's replay history" },
|
|
62
62
|
{ "command": "read-models snapshots", "reason": "reads snapshots of one read model instance" },
|
|
63
63
|
{ "command": "recommendations list", "reason": "lists active recommendations" },
|
|
64
|
+
{ "command": "streams closed list", "reason": "lists closed stream scopes and their closure metadata without changing them" },
|
|
64
65
|
{ "command": "subscriptions list", "reason": "lists event store subscriptions" },
|
|
65
66
|
{ "command": "users list", "reason": "lists registered users" }
|
|
66
67
|
],
|
|
@@ -68,13 +69,16 @@
|
|
|
68
69
|
{ "command": "observers replay", "effect": "replays an observer from the beginning of the event log, resetting all state it has built" },
|
|
69
70
|
{ "command": "observers replay-partition", "effect": "replays one partition of an observer from the beginning, resetting that partition's state" },
|
|
70
71
|
{ "command": "observers retry-partition", "effect": "retries a failed partition from the sequence number where it failed" },
|
|
72
|
+
{ "command": "observers retry-failed-partitions", "effect": "starts bounded-batch recovery of every failed partition of an observer; acceptance does not mean recovery finished" },
|
|
71
73
|
{ "command": "observers clear-quarantine", "effect": "clears observer-level quarantine only, not a partition's quarantine" },
|
|
72
74
|
{ "command": "observers clear-partition-quarantine", "effect": "clears a failed partition's quarantine and resets its retry budget; retries the handler unless --no-retry is given" },
|
|
73
75
|
{ "command": "observers remove", "effect": "deletes an observer's definition, state, handled counts, failed partitions and projection definition across every namespace of the event store; leaves read model data" },
|
|
74
76
|
{ "command": "jobs stop", "effect": "stops a running background job" },
|
|
75
77
|
{ "command": "jobs resume", "effect": "resumes a stopped background job" },
|
|
78
|
+
{ "command": "jobs delete", "effect": "requests irreversible deletion of a job and its retained state; acceptance does not confirm completed removal" },
|
|
76
79
|
{ "command": "recommendations perform", "effect": "executes a server recommendation, such as a schema or replay maintenance action" },
|
|
77
80
|
{ "command": "recommendations ignore", "effect": "marks a recommendation as ignored so it no longer appears" },
|
|
81
|
+
{ "command": "streams closed reopen", "effect": "reopens one exact manual stream closure and records the authenticated actor and repair reason in a StreamScopeReopened system event; event-driven closures are refused" },
|
|
78
82
|
{ "command": "subscriptions add", "effect": "adds an event store subscription to a target event store" },
|
|
79
83
|
{ "command": "subscriptions remove", "effect": "removes an event store subscription from a target event store" },
|
|
80
84
|
{ "command": "users add", "effect": "adds a user to the server's user store" },
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
}
|
|
64
64
|
],
|
|
65
65
|
"synced": {
|
|
66
|
-
"version": "22.
|
|
67
|
-
"date": "2026-10-
|
|
68
|
-
"by": "Cratis/AI#
|
|
66
|
+
"version": "22.139.0",
|
|
67
|
+
"date": "2026-10-25",
|
|
68
|
+
"by": "Cratis/AI#820"
|
|
69
69
|
},
|
|
70
70
|
"covers": {
|
|
71
71
|
"skills": [
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"vertical-slices.md"
|
|
100
100
|
]
|
|
101
101
|
},
|
|
102
|
-
"notes": "Reviewed releases 22.
|
|
102
|
+
"notes": "Reviewed all 53 releases 22.117.1-22.139.0 at tagged sources. Corrected public-only query discovery, single observations, pre-transport occurrence append refusal, assignable pipeline responses, concept binding/annotations, protected validator construction and key metadata; added live query/calendar, upsert, scenario, authentication and proxy-generation guidance. Existing response validation, React lifetimes, tenant routing and storage resolution guidance retained after reviewing fixes; internal/dependency changes need no extra consumer action. Screenplay/Scene/Stage deltas belong to their owning lanes. No live runtime probe; prior floor established in Cratis/AI#773."
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
105
|
"id": "ai",
|
|
@@ -125,9 +125,9 @@
|
|
|
125
125
|
}
|
|
126
126
|
],
|
|
127
127
|
"synced": {
|
|
128
|
-
"version": "3.
|
|
129
|
-
"date": "2026-10-
|
|
130
|
-
"by": "Cratis/AI#
|
|
128
|
+
"version": "3.100.0",
|
|
129
|
+
"date": "2026-10-25",
|
|
130
|
+
"by": "Cratis/AI#820"
|
|
131
131
|
},
|
|
132
132
|
"covers": {
|
|
133
133
|
"skills": [
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
],
|
|
159
159
|
"rules": []
|
|
160
160
|
},
|
|
161
|
-
"notes": "
|
|
161
|
+
"notes": "Reviewed all 35 releases 3.76.0-3.100.0 at tagged sources: refusal-aware job actions, observer-wide retry, exact manual stream repair, retained-job deletion, bounded namespace metadata comparison, release notes/cached templates, Pi adapters/profile MCP and isolated AI conformance/install-only checks. Updated CLI operations and terminal Workbench guidance. Multi-service context/orchestration unchanged. Reviewed bundled render/run changes through Stage 4.196.0 and Screenplay 4.240.2, ESM v29/v30 admission, reducer migration, executable target validation, model-root resolution and sample packs; Screenplay/Stage/Prologue skill edits reserved for their owning lanes. No live store, render, harness or browser probe. Prior floor established in Cratis/AI#752."
|
|
162
162
|
},
|
|
163
163
|
{
|
|
164
164
|
"id": "screenplay",
|
|
@@ -229,9 +229,9 @@
|
|
|
229
229
|
"repository": "Cratis/Orleans",
|
|
230
230
|
"packages": [],
|
|
231
231
|
"synced": {
|
|
232
|
-
"version": "1.
|
|
233
|
-
"date": "2026-10-
|
|
234
|
-
"by": "Cratis/AI#
|
|
232
|
+
"version": "1.30.0",
|
|
233
|
+
"date": "2026-10-25",
|
|
234
|
+
"by": "Cratis/AI#820"
|
|
235
235
|
},
|
|
236
236
|
"covers": {
|
|
237
237
|
"skills": [
|
|
@@ -243,7 +243,7 @@
|
|
|
243
243
|
]
|
|
244
244
|
},
|
|
245
245
|
"coverage": "tracked",
|
|
246
|
-
"notes": "
|
|
246
|
+
"notes": "Reviewed all 33 releases 1.17.0-1.30.0 at tagged sources: local IJobs and recovery keys, activation-safe callbacks and drain/admission, bounded retries/test runs, durable plans and initialization hooks, finalized-stop/outcome storage and independent placement/metadata throttle; corrected jobs and Orleans hosting guidance. Internal fixes require no extra consumer action; no live silo probe. Prior floor established in Cratis/AI#620."
|
|
247
247
|
},
|
|
248
248
|
{
|
|
249
249
|
"id": "keldakraft",
|
|
@@ -10,7 +10,7 @@ paths:
|
|
|
10
10
|
> **⚠️ APPLIES ONLY TO PROJECTS USING MICROSOFT ORLEANS**
|
|
11
11
|
> If your project does not reference `Microsoft.Orleans` or any Orleans packages, **ignore this entire file**. These rules are irrelevant outside of Orleans contexts.
|
|
12
12
|
|
|
13
|
-
In the Cratis organization, Orleans is the Chronicle kernel's actor runtime (`Source/Kernel/`). The kernel conventions below are verified against `Cratis/Chronicle` **v19.50.0**. Shared hosting and jobs come from `Cratis/Orleans` **v1.
|
|
13
|
+
In the Cratis organization, Orleans is the Chronicle kernel's actor runtime (`Source/Kernel/`). The kernel conventions below are verified against `Cratis/Chronicle` **v19.50.0**. Shared hosting and jobs come from `Cratis/Orleans` **v1.30.0**; use its setup for other services that co-host a silo.
|
|
14
14
|
|
|
15
15
|
## General
|
|
16
16
|
|
|
@@ -49,7 +49,7 @@ framework/service guidance; they do not impose application slice architecture.
|
|
|
49
49
|
|
|
50
50
|
For service authors, `Cratis.Orleans` supplies hosting, grain serializers, and the
|
|
51
51
|
shared job system without requiring Chronicle. With `Cratis.Orleans` and
|
|
52
|
-
`Cratis.Orleans.Storage.MongoDB` 1.
|
|
52
|
+
`Cratis.Orleans.Storage.MongoDB` 1.30.0, this host uses durable MongoDB membership
|
|
53
53
|
and jobs storage (a local MongoDB server is required):
|
|
54
54
|
|
|
55
55
|
```csharp
|
|
@@ -80,6 +80,14 @@ app.Run();
|
|
|
80
80
|
- Register jobs storage separately: `AddCratisOrleansMongoDBJobsStorage`,
|
|
81
81
|
`AddCratisOrleansSqlJobsStorage` (`Cratis.Orleans.Setup`), or
|
|
82
82
|
`AddCratisOrleansInMemoryJobsStorage` (`Cratis.Orleans.Storage`).
|
|
83
|
+
- Use the local `IJobs` service to start/stop/delete typed jobs during rolling
|
|
84
|
+
upgrades; generic manager calls can land on a release that does not know the
|
|
85
|
+
type. Provide singleton `IJobsManagerKeys` on every silo for all host-owned
|
|
86
|
+
scope/namespace pairs; process-local discovery is not a recovery catalog.
|
|
87
|
+
- Controller and worker placement are separate opt-ins through `AddJobPlacement`
|
|
88
|
+
and `AddJobStepPlacement`. Enable them only after every silo supports the
|
|
89
|
+
strategies and register them identically. Worker throttling is not admission;
|
|
90
|
+
see **cratis-orleans-jobs** for drain-before-replacement and metadata limits.
|
|
83
91
|
- Give grain implementations a unique `[GrainType("unique-name")]`. Orleans'
|
|
84
92
|
default type name derives from the class name alone; a collision across
|
|
85
93
|
assemblies prevents silo startup.
|
|
@@ -257,7 +257,7 @@ public record AuthorRegistered(AuthorName Name);
|
|
|
257
257
|
|
|
258
258
|
## Read Models and Queries
|
|
259
259
|
|
|
260
|
-
**[contract]** `[ReadModel]` records expose eligible public
|
|
260
|
+
**[contract]** `[ReadModel]` records expose eligible public static methods as queries. Requires Arc 22.121.0 for non-public methods to be excluded even with `[Query]` (ARC0029 warns); older versions also expose eligible internal statics. Keep helpers outside the read model when supporting those versions. Dependencies are plain method parameters (no `[FromServices]`). For application code, start with model-bound mappings, use fluent when the definition needs structure, and choose reducer code when transitions read better as code. For step-by-step creation, invoke the **cratis-chronicle-read-model** skill.
|
|
261
261
|
|
|
262
262
|
Use `[ReadModel(RequireQueryAttribute = true)]` with `[Query]` on intended
|
|
263
263
|
endpoints when folds exist (ARC0026/ARCCHR0019). `[Query]` alone does not hide
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Query and decision scenarios
|
|
2
2
|
|
|
3
|
+
## Contents
|
|
4
|
+
|
|
5
|
+
- Query pipeline
|
|
6
|
+
- Guarded decisions
|
|
7
|
+
- Sources
|
|
8
|
+
|
|
3
9
|
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
4
10
|
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
5
11
|
|
|
@@ -52,6 +58,13 @@ not a caller-supplied tenant filter. Compare whole returned items when the contr
|
|
|
52
58
|
value; otherwise assert each named value. A query backed by a database still needs that provider's
|
|
53
59
|
integration setup: scenario pins do not replace arbitrary stores.
|
|
54
60
|
|
|
61
|
+
`QueryScenario` owns and disposes its provider. Dispose the scenario (prefer
|
|
62
|
+
`DisposeAsync` for async-owned resources) as well as individual observations.
|
|
63
|
+
Register dependencies in `Services` before the first execution. Scenario
|
|
64
|
+
execution runs the query through Arc's hosted query pipeline and establishes context
|
|
65
|
+
before `Provide`/`Handle` on the command path. A direct static query call does not
|
|
66
|
+
cover those pipeline hooks.
|
|
67
|
+
|
|
55
68
|
## Guarded decisions
|
|
56
69
|
|
|
57
70
|
Create `new CommandScenario<TCommand>()`. It selects Chronicle's in-process decision
|
|
@@ -87,6 +100,12 @@ read-model dictionary is the only backing are **legacy-mode** warnings, not a
|
|
|
87
100
|
reason to fake protected decision readers. Keep non-key enumeration/provider
|
|
88
101
|
boundaries explicit in either mode; decision mode is not a MongoDB substitute.
|
|
89
102
|
|
|
103
|
+
The default Chronicle-backed `CommandScenario` discovers constraints
|
|
104
|
+
automatically. Assert real constraint refusals instead of disabling them. Do not
|
|
105
|
+
subclass or substitute Chronicle's `EventScenario`; use its public
|
|
106
|
+
seeding/assertion surface or explicit `UseDecisionReads` mode, not a synthetic
|
|
107
|
+
EventScenario facade.
|
|
108
|
+
|
|
90
109
|
## Sources
|
|
91
110
|
|
|
92
111
|
- [Query scenarios](https://github.com/Cratis/Arc/blob/main/Documentation/backend/csharp/testing/query-scenario.md)
|
|
@@ -179,6 +179,14 @@ filters, and system-execution.
|
|
|
179
179
|
`Task<AuthenticationResult> HandleAuthentication(IHttpRequestContext context)`.
|
|
180
180
|
Implement it and it is discovered — there is **no** `IArcBuilder` extension and
|
|
181
181
|
**no** options type for authentication.
|
|
182
|
+
Requires Arc 22.139.0 for handlers to opt out of anonymous endpoints through
|
|
183
|
+
`AppliesToAnonymousEndpoints => false`. The default is `true`: existing handlers
|
|
184
|
+
keep running and may establish an identity even when no credential is required.
|
|
185
|
+
Opt out when a handler only rejects credentials and contributes no identity on
|
|
186
|
+
those endpoints. A handler that still runs can inspect the endpoint's anonymous
|
|
187
|
+
metadata and return `AuthenticationResult.Anonymous` instead of rejecting a
|
|
188
|
+
credential the endpoint never required. Specify anonymous and protected requests;
|
|
189
|
+
anonymous access does not bypass other authorization boundaries.
|
|
182
190
|
|
|
183
191
|
Arc ships `MicrosoftIdentityPlatformAuthenticationHandler` in `Arc.Core`, which
|
|
184
192
|
reads the forwarded principal. See
|
|
@@ -16,7 +16,7 @@ Reference the build package from the project that contains the commands,
|
|
|
16
16
|
queries and read models, and set an output path:
|
|
17
17
|
|
|
18
18
|
```xml
|
|
19
|
-
<PackageReference Include="Cratis.Arc.ProxyGenerator.Build" Version="22.
|
|
19
|
+
<PackageReference Include="Cratis.Arc.ProxyGenerator.Build" Version="22.139.0" />
|
|
20
20
|
|
|
21
21
|
<PropertyGroup>
|
|
22
22
|
<CratisProxiesOutputPath>$(MSBuildThisFileDirectory)../<Web>/src/api</CratisProxiesOutputPath>
|
|
@@ -47,6 +47,8 @@ switches without a declared default are off when absent.
|
|
|
47
47
|
| --- | --- | --- |
|
|
48
48
|
| `CratisProxiesOutputPath` | empty | Where to write. Empty disables generation entirely |
|
|
49
49
|
| `CratisProxiesInputAssembly` | empty | Overrides the assembly to read |
|
|
50
|
+
| `CratisProxiesIncremental` | `true` | Requires Arc 22.138.0; reuse unchanged owned output; set `false` to regenerate every build |
|
|
51
|
+
| `CratisProxiesFileHeader` | project setting | Requires Arc 22.123.0; otherwise uses `.editorconfig` `file_header_template`, or no header; `none` disables it |
|
|
50
52
|
| `CratisProxiesSegmentsToSkip` | empty | Leading namespace segments to drop from the output path |
|
|
51
53
|
| `CratisProxiesLibraryMode` | `false` | Generates for a library rather than an application |
|
|
52
54
|
| `CratisProxiesSkipOutputDeletion` | **`true`** | Already the default — the generator does not wipe the output folder |
|
|
@@ -70,6 +72,13 @@ are optional. Sources:
|
|
|
70
72
|
`Tools/ProxyGenerator.Build/build/Cratis.Arc.ProxyGenerator.Build.targets` under
|
|
71
73
|
`Source/DotNET`.
|
|
72
74
|
|
|
75
|
+
Incremental generation fingerprints assemblies, generator/configuration, validation metadata
|
|
76
|
+
and owned output. A changed or missing generated file forces regeneration, as
|
|
77
|
+
does a previous warning so diagnostics remain visible. It does not make a
|
|
78
|
+
hand-edited generated file an input. Configure `file_header_template` explicitly
|
|
79
|
+
if you previously relied on the built-in Cratis MIT header; it is no longer
|
|
80
|
+
inserted without configuration.
|
|
81
|
+
|
|
73
82
|
Item groups the target also forwards: `AssemblyToPackageMapping`,
|
|
74
83
|
`ExcludeType`, `ExcludeNamespace`, `NamespaceRoot`, and `TypeToTsType`.
|
|
75
84
|
|
|
@@ -101,8 +101,9 @@ if (result.IsSuccess)
|
|
|
101
101
|
}
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
`Execute<TResult>` accepts an assignable response, such as an array requested
|
|
105
|
+
as `IEnumerable<T>` or a concrete value requested as an interface/base type.
|
|
106
|
+
⚠️ An incompatible non-null response still throws `InvalidCastException`. When the handler returns no response, or the
|
|
106
107
|
command failed for any reason, `Response` is `default` — a failed result never
|
|
107
108
|
carries a response, because the pipeline clears it once the execution scopes
|
|
108
109
|
have completed.
|
package/package/corpus/skills/cratis-arc-command-execution/references/occurrence-commands.md
CHANGED
|
@@ -61,12 +61,13 @@ key where that service supports it; otherwise an external effect can repeat.
|
|
|
61
61
|
Return events from the command, or use transactional appends. Do not complete a
|
|
62
62
|
stream or commit an aggregate early inside the executor's command.
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
Requires Arc 22.118.0 for the keyed executor to refuse immediate appends
|
|
65
|
+
**before transport**, with neither appended events nor an effect receipt. Arc
|
|
66
|
+
22.117.x detected `OccurrenceCommandAppendedImmediately` only after an append
|
|
67
|
+
could escape; upgrade rather than treating that detection as retry safety.
|
|
68
|
+
Use `.Transactional` or returned events, and assert that a forbidden direct
|
|
69
|
+
append writes nothing. For a custom non-executor boundary, read
|
|
70
|
+
**cratis-chronicle-client-dotnet**'s immediate-append refusal reference.
|
|
70
71
|
|
|
71
72
|
## Verify
|
|
72
73
|
|
|
@@ -142,6 +142,11 @@ public class TransferFundsValidator : CommandValidator<TransferFunds>
|
|
|
142
142
|
- A validator that itself throws does not become a 500 — Arc catches it and
|
|
143
143
|
emits a validation error with reason `ValidatorFailed`, returning 400.
|
|
144
144
|
|
|
145
|
+
Read [protected validator construction](references/protected-validator-construction.md)
|
|
146
|
+
for plain read-model constructor inputs under `On`, the separate untrimmed-JIT
|
|
147
|
+
provider opt-in for decision tokens outside `On`, and generated composite-key
|
|
148
|
+
metadata. Do not conflate metadata coverage with validator certification.
|
|
149
|
+
|
|
145
150
|
## Step 4 — Data the handler needs: `Provide()`
|
|
146
151
|
|
|
147
152
|
`Provide()` runs after authorization and validation and before `Handle()`. Its
|
|
@@ -15,6 +15,14 @@ with `DateTime command property '<member>' requires an explicit offset or Z.`
|
|
|
15
15
|
`DateTimeOffset` retains the input offset; `DateTime` becomes UTC. The attribute
|
|
16
16
|
is in `Cratis.Arc.Commands`.
|
|
17
17
|
|
|
18
|
+
Requires Arc 22.120.0 for concepts over `DateTime` or `DateTimeOffset` with
|
|
19
|
+
Arc's default JSON options. Concepts over `DateOnly` are refused as a
|
|
20
|
+
configuration error: calendar dates have no offset. The marker affects
|
|
21
|
+
JSON-bound properties, including nested collection elements, not query strings
|
|
22
|
+
or route scalars. Use reflection-based JSON metadata for these properties;
|
|
23
|
+
System.Text.Json source-generated metadata does not support derived
|
|
24
|
+
`JsonConverterAttribute` markers.
|
|
25
|
+
|
|
18
26
|
## Malformed bodies still authorize
|
|
19
27
|
|
|
20
28
|
Requires Arc 22.110.0 for authorization-before-binding-failure handling on
|
|
@@ -31,6 +39,15 @@ policies can run; built-in role, claim, name and authenticated-user requirements
|
|
|
31
39
|
are already resource-free. Do not mark a requirement resource-free if it reads
|
|
32
40
|
the command.
|
|
33
41
|
|
|
42
|
+
## Annotations on concepts
|
|
43
|
+
|
|
44
|
+
Requires Arc 22.121.2 for data annotations on command properties and model-bound
|
|
45
|
+
query parameters to validate a concept's wrapped value: `[Required]` rejects an
|
|
46
|
+
empty wrapped string, `[Range]` checks the primitive, and `[StringLength]` does
|
|
47
|
+
not throw on the wrapper. This uses generated validation metadata; types without
|
|
48
|
+
that metadata (including external assemblies) keep the previous behavior. Do
|
|
49
|
+
not assume annotations replace a concept's reusable validator.
|
|
50
|
+
|
|
34
51
|
## Validation members are field paths
|
|
35
52
|
|
|
36
53
|
Requires Arc 22.115.0 for **all** `ValidationResult.Members` paths to be
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
2
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
3
|
+
|
|
4
|
+
# Protected validator construction and key metadata
|
|
5
|
+
|
|
6
|
+
## Plain read models under On
|
|
7
|
+
|
|
8
|
+
Requires Arc 22.106.0 for plain Chronicle read-model dependencies in
|
|
9
|
+
`CommandValidator<T>` constructors under read protection `On`. Those reads share
|
|
10
|
+
the command's fold cache and append guard with `Provide`/`Handle`. Let convention
|
|
11
|
+
discovery register a transient validator. Scoped/singleton/factory/instance
|
|
12
|
+
registrations are refused; a plain transient self-binding is also allowed.
|
|
13
|
+
Ineligible construction fails closed; never fall back to unguarded reads. Use
|
|
14
|
+
`[LookUpBy(nameof(Command.Property))]` on each constructor read for multiple
|
|
15
|
+
keys. Another source/stream can instead be read explicitly in `Provide`.
|
|
16
|
+
|
|
17
|
+
## Explicit decision tokens outside On
|
|
18
|
+
|
|
19
|
+
Requires Arc 22.126.0 for the opt-in construction of validators taking
|
|
20
|
+
`DecisionRead<T>` or command-aware `IDecisionReads` on `[ProtectedDecision]`
|
|
21
|
+
commands outside `On`. This is **untrimmed JIT only**, not trimmed JIT or
|
|
22
|
+
NativeAOT, and not a switch making every DI container trusted.
|
|
23
|
+
|
|
24
|
+
Install `ProtectedDecisionServiceProviderFactory` from
|
|
25
|
+
`Cratis.Arc.DependencyInjection` as the host's actual Microsoft DI factory
|
|
26
|
+
**after** `AddCratisArc`, passing the desired `ServiceProviderOptions` explicitly.
|
|
27
|
+
Without them both `ValidateScopes` and `ValidateOnBuild` default to false; host
|
|
28
|
+
Development settings are not inherited. `BuildProtectedDecisionServiceProvider`
|
|
29
|
+
is for independently owned service collections, not a second root inside the
|
|
30
|
+
running host. Execute in a native child scope (or the pipeline's scope-owning
|
|
31
|
+
overload), never directly on that root. Default `CommandScenario` does not opt in.
|
|
32
|
+
|
|
33
|
+
Register Chronicle through Arc's `WithChronicle`. Do not replace readers or
|
|
34
|
+
re-register tokens. Arc records origins at root construction; copied descriptors,
|
|
35
|
+
a wrapper provider, custom container or later collection mutation cannot acquire
|
|
36
|
+
that evidence.
|
|
37
|
+
|
|
38
|
+
Every validator/collaborator needs exactly one public constructor and a fully
|
|
39
|
+
inspectable transient graph. Arc constructs fresh instances for the invocation.
|
|
40
|
+
It refuses scoped/singleton validators or collaborators, application validator
|
|
41
|
+
bindings, factory/instance dependencies, cycles, keyed/open generic/deferred or
|
|
42
|
+
optional dependencies, provider/scope accessors and collections (including token
|
|
43
|
+
collections). Plain models and raw `IReadModels`, `IEventStore` or `IEventLog`
|
|
44
|
+
are refused on this path: use tokens or choose `On`.
|
|
45
|
+
|
|
46
|
+
Requires Arc 22.126.0 for
|
|
47
|
+
`services.AddProtectedDecisionValidator<TValidator>(configure)`. Use it instead
|
|
48
|
+
of an application DI factory when adding rules. The callback runs once on the
|
|
49
|
+
actual fresh instance before validation; thrown callbacks fail the command and
|
|
50
|
+
callback denials are not bypassed. It can replace one Arc convention binding,
|
|
51
|
+
not an explicit application registration. Extra explicit bindings refuse the
|
|
52
|
+
protected path. Ordinary commands still use normal constructor resolution.
|
|
53
|
+
|
|
54
|
+
Arc checks rule/component identities before invocation and refuses changed or
|
|
55
|
+
cross-scope/reused instances. Construction and cleanup failures block before
|
|
56
|
+
commit even with allowed-severity overrides. Arc disposes owned validators and
|
|
57
|
+
collaborators in reverse order; DI owns read leaves. This is not a sandbox for
|
|
58
|
+
trusted constructor/callback code, mutable captures, static reads or nested-child
|
|
59
|
+
rule changes. `Validate` stays advisory and creates a separate invocation; only
|
|
60
|
+
Execute enrolls its decision for append-time checking. Under `Off`, choose the
|
|
61
|
+
blocking severity needed for business denials.
|
|
62
|
+
|
|
63
|
+
## Generated key metadata is not validator certification
|
|
64
|
+
|
|
65
|
+
`CommandReadModelKeys` registers command property/model-key metadata.
|
|
66
|
+
Requires Arc 22.125.0 for generated composite-key descriptors and
|
|
67
|
+
`ProtectedReadModelKeyAccessors`, which registers exact-type composite members.
|
|
68
|
+
These optimize matching; they do not authorize a read or certify a validator's
|
|
69
|
+
dependency graph.
|
|
70
|
+
|
|
71
|
+
Known concrete composite keys receive generated complete descriptors. For an
|
|
72
|
+
external/runtime key, register `ProtectedReadModelKeyAccessors.Register<T>` with
|
|
73
|
+
every public readable non-indexed instance property, including inherited ones,
|
|
74
|
+
before commands run. Generated metadata uses `DeclareKeyTypes` for startup
|
|
75
|
+
coverage checks; that is not a replacement for registering member accessors.
|
|
76
|
+
Duplicate registration fails. Do not omit a member or treat a
|
|
77
|
+
base descriptor as covering a derived runtime type. Missing declared metadata
|
|
78
|
+
fails with `ProtectedReadModelKeyAccessorMissing`.
|
|
79
|
+
|
|
80
|
+
Requires Arc 22.134.0 for `ReadProtection:RequireGeneratedMetadata` (false by
|
|
81
|
+
default). Enabling it rejects missing command/model metadata instead of using
|
|
82
|
+
reflection. `ProtectedReadModelKeyOptions.Strict` is also false by default;
|
|
83
|
+
strict composite matching or a restricted runtime requires exact accessors.
|
|
84
|
+
Untrimmed JIT can use the compatibility reflection path when strict mode is off.
|
|
85
|
+
ARC0030 is the composite coverage warning. Do not suppress it or promise
|
|
86
|
+
NativeAOT support for the unrelated dynamic validator opt-in.
|
|
87
|
+
|
|
88
|
+
## Specify the boundary
|
|
89
|
+
|
|
90
|
+
Cover a fresh allowed/denied validator, callback-added denial and exception,
|
|
91
|
+
refused DI graphs and cleanup failure, and repeated validation/execution without
|
|
92
|
+
reusing an instance. For keys, cover inherited/flattened members, exact derived
|
|
93
|
+
types, missing metadata under strict settings and generated versus compatibility
|
|
94
|
+
matching. An advisory Validate success is not proof that Execute commits.
|
|
@@ -187,6 +187,24 @@ Mark the corresponding property `[Json]`.
|
|
|
187
187
|
`migrationBuilder.GetDatabaseType()` is available when a migration genuinely has
|
|
188
188
|
to branch per provider.
|
|
189
189
|
|
|
190
|
+
## Tracked and native upserts
|
|
191
|
+
|
|
192
|
+
Requires Arc 22.135.0 for `DbSet<TEntity>.UpsertAsync(item, cancellationToken)`.
|
|
193
|
+
On supported SQL Server, PostgreSQL and SQLite mappings it executes an immediate,
|
|
194
|
+
parameterized native upsert matching the complete primary key, including composite
|
|
195
|
+
keys and value converters. Native key matching and writing are atomic. It uses
|
|
196
|
+
the context's connection/current transaction, bypasses tracking and does not
|
|
197
|
+
refresh generated values or already tracked instances. It adds no retry policy.
|
|
198
|
+
|
|
199
|
+
Use application-assigned keys and single-table scalar mappings. Unsupported
|
|
200
|
+
providers or mappings (including unset generated keys, SQL Server identity
|
|
201
|
+
columns, navigations, owned entities, shadow properties, inheritance and multiple
|
|
202
|
+
tables) fall back to tracked persistence and require `SaveChangesAsync`. The
|
|
203
|
+
existing `Upsert(item)` always uses tracked read-then-write persistence and also
|
|
204
|
+
requires saving; that path is not an atomic database upsert. Choose it explicitly
|
|
205
|
+
when deferred persistence is required. Do not rely on fallback for concurrency
|
|
206
|
+
protection or assume the native path flushes pending tracked changes.
|
|
207
|
+
|
|
190
208
|
## Observing an EF Core read model
|
|
191
209
|
|
|
192
210
|
`DbSet<T>` gains `Observe(filter?, configure?)`, `ObserveSingle(filter?, configure?)`
|
|
@@ -56,10 +56,12 @@ Sources: `Arc.Core/Queries/QueryStringQueryRequestReader.cs`,
|
|
|
56
56
|
|
|
57
57
|
## Which return shapes page
|
|
58
58
|
|
|
59
|
-
A query is an eligible public
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
A query is an eligible public static method on the `[ReadModel]` type,
|
|
60
|
+
discovered by its return type.
|
|
61
|
+
Requires Arc 22.121.0 for non-public statics to be
|
|
62
|
+
excluded even with `[Query]` (ARC0029 warns); older Arc exposes internal methods
|
|
63
|
+
as endpoints too. Read **cratis-chronicle-read-model**'s query reference for
|
|
64
|
+
explicit `[Query]` discovery and older-version limits.
|
|
63
65
|
|
|
64
66
|
| Return | Paged? | How |
|
|
65
67
|
| --- | --- | --- |
|
|
@@ -174,6 +176,11 @@ For composed MongoDB/EF observations, pass `ignoreQueryContext: true` to
|
|
|
174
176
|
auxiliary sources so only the primary consumes client paging/sorting and writes
|
|
175
177
|
totals. Single-instance observations emit `default` when absent, deleted or
|
|
176
178
|
outside the filter; guard `hasData`/`isReady` in React.
|
|
179
|
+
Requires Arc 22.117.1 for
|
|
180
|
+
`ObserveSingle`/`ObserveById` never to page or count; their retained
|
|
181
|
+
`ignoreQueryContext` argument has no effect. Use collection results for paging.
|
|
182
|
+
Read [live query processing](references/live-query-processing.md) for shared
|
|
183
|
+
MongoDB watches, emitted-value interception and calendar-bound answers.
|
|
177
184
|
Source: [MongoDB observations](https://github.com/Cratis/Arc/blob/main/Documentation/backend/csharp/mongodb/observing-collections.md).
|
|
178
185
|
|
|
179
186
|
## Consume it from the frontend
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
2
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
3
|
+
|
|
4
|
+
# Live query processing
|
|
5
|
+
|
|
6
|
+
## Shared MongoDB watches
|
|
7
|
+
|
|
8
|
+
Requires Arc 22.136.0. MongoDB `Observe()` uses a shared database-level watch keyed by deployment and
|
|
9
|
+
database rather than opening a collection watch for every observer. Each
|
|
10
|
+
observation keeps its filter, paging and tenant context.
|
|
11
|
+
Requires Arc 22.137.0 for observations of collections created by the caller's
|
|
12
|
+
own `MongoClient` to receive changes. The watcher derives the deployment key
|
|
13
|
+
from the database client instead of requiring prior registration. Joined observations coalesce refresh notifications into one pending
|
|
14
|
+
refresh and re-read every joined source. Snapshot read failures receive a
|
|
15
|
+
bounded retry burst; exhaustion waits for a later notification rather than
|
|
16
|
+
spinning or buffering unbounded work.
|
|
17
|
+
|
|
18
|
+
Watch reconnects resume from the token when possible and request a new snapshot
|
|
19
|
+
when the token cannot resume. The watch reopens before asking for snapshots so
|
|
20
|
+
writes during the read are still observed. Disposing one observer does not stop
|
|
21
|
+
the shared watch; disposing its subject or losing the last subscriber stops that
|
|
22
|
+
observation's refresh work and prevents a late emission. Shared watches do not
|
|
23
|
+
replace each subscription's cancellation and disposal.
|
|
24
|
+
|
|
25
|
+
## Intercept the emitted model, not its wrapper
|
|
26
|
+
|
|
27
|
+
Implement `Cratis.Arc.Queries.IInterceptReadModel<T>` with
|
|
28
|
+
`Task<T> Intercept(T readModel)`. Return a copy when transforming data that another
|
|
29
|
+
subscriber could share. A non-abstract open generic `IInterceptReadModel<T>`
|
|
30
|
+
implementation must target exactly its own type parameter; closed
|
|
31
|
+
implementations also work.
|
|
32
|
+
|
|
33
|
+
Interception runs for supported direct/hub streaming emissions and observable
|
|
34
|
+
HTTP snapshot/wait results in the requesting subscriber's scope and identity. Interceptors run before emission guards; a thrown interceptor
|
|
35
|
+
fails the result rather than exposing the untransformed value. Ordinary model
|
|
36
|
+
queries and Arc-wrapped MVC GETs also intercept results; `[AspNetResult]` opts
|
|
37
|
+
out of that result processing. Interceptors are transformations, **not access
|
|
38
|
+
control**. Filter rows/fields and authorize independently on every exposed path,
|
|
39
|
+
and specify snapshot, SSE/WebSocket and hub paths separately.
|
|
40
|
+
|
|
41
|
+
## Date-sensitive answers without data changes
|
|
42
|
+
|
|
43
|
+
Requires Arc 22.138.0 for `Cratis.Arc.Calendar`. Inject `IApplicationCalendar` and
|
|
44
|
+
pass an instant to `Read`; it does not read the clock. Zone precedence is an
|
|
45
|
+
operation-declared zone, then the first non-null `ICanProvideTenantCalendar`
|
|
46
|
+
answer, then `Cratis:Arc:Calendar:TimeZone` (default `UTC`). Headers and query
|
|
47
|
+
arguments cannot choose it. Configured and tenant-provider ids must be usable
|
|
48
|
+
IANA ids: unknown, Windows-only and nonzero fixed-offset ids fail with
|
|
49
|
+
`TimeZoneIsNotAnIanaZone`,
|
|
50
|
+
never a silent UTC fallback. Containers need time-zone data (`tzdata`).
|
|
51
|
+
|
|
52
|
+
A live "due today" answer needs candidates observed **without a clock-dependent
|
|
53
|
+
store filter**, or tomorrow's members will never enter the source. Use
|
|
54
|
+
`IClockBoundaries.Begin(tenant, operationZone, token)` to obtain a
|
|
55
|
+
`ClockBoundaryContext`, then `candidates.ReevaluateAtBoundaries(context,
|
|
56
|
+
(candidates, reading) => new ClockEvaluation<TAnswer>(answer, nextBoundary))`.
|
|
57
|
+
`LocalDateChanges.NextLocalDateChange(reading.Zone, reading.Instant)` handles
|
|
58
|
+
midnight, skipped dates and backwards date transitions. `reading.Today.AddDays`
|
|
59
|
+
steps calendar days; subtracting elapsed hours from the instant is different
|
|
60
|
+
across daylight-saving changes.
|
|
61
|
+
|
|
62
|
+
Every next boundary must be strictly after the evaluated instant or null;
|
|
63
|
+
otherwise the stream fails with `ClockBoundaryIsNotInTheFuture`. The first result
|
|
64
|
+
always emits; equal subsequent answers do not (collections compare in order).
|
|
65
|
+
Evaluation is serialized in the context's tenant. The zone is fixed at Begin;
|
|
66
|
+
application changes require restart and tenant-zone changes affect new
|
|
67
|
+
subscriptions. The operator connects on first subscription and disposes timer
|
|
68
|
+
and candidates on last unsubscribe, cancellation, error or completion. It does
|
|
69
|
+
not accept `OnNext`. Use the registered `TimeProvider` and a `FakeTimeProvider`
|
|
70
|
+
with `Advance` to specify boundary changes without sleeps. This is not a
|
|
71
|
+
continuously ticking `now` or an advanceable clock in query scenarios.
|
|
@@ -60,6 +60,10 @@ Two things worth knowing before running it:
|
|
|
60
60
|
connection guidance in a repository-owned project context file; never patch
|
|
61
61
|
packaged or generated bytes.
|
|
62
62
|
|
|
63
|
+
Read [project tooling](references/project-tooling.md) for embedded release notes,
|
|
64
|
+
cached templates, managed MCP registration and install-only versus harness AI
|
|
65
|
+
conformance checks. Updating instructions does not run those behavior checks.
|
|
66
|
+
|
|
63
67
|
## Reach the right server
|
|
64
68
|
|
|
65
69
|
The connection string resolves in this exact order:
|
|
@@ -180,6 +184,10 @@ needs Chronicle 19.29.0 or later, resets the retry budget and retries unless `--
|
|
|
180
184
|
is given; handler side effects may repeat. `retry-partition` exits non-zero
|
|
181
185
|
when quarantined or not failed. `jobs resume` resumes Stopped jobs, not Failed.
|
|
182
186
|
|
|
187
|
+
Read [recovery outcomes and namespace comparison](references/recovery-and-comparison.md)
|
|
188
|
+
for refusal-aware job actions, retained-job deletion, observer-wide failed-partition
|
|
189
|
+
recovery, exact manual stream reopening and bounded metadata comparison.
|
|
190
|
+
|
|
183
191
|
## Before you change anything
|
|
184
192
|
|
|
185
193
|
Default to read-only inspection. A request to diagnose a live store does not
|
|
@@ -188,12 +196,13 @@ authorize replay, retry, removal, quarantine clearing, or another mutation.
|
|
|
188
196
|
These commands mutate the running store:
|
|
189
197
|
|
|
190
198
|
- `chronicle observers replay`, `replay-partition`, `retry-partition`,
|
|
191
|
-
`clear-quarantine`
|
|
199
|
+
`retry-failed-partitions`, `clear-quarantine`
|
|
192
200
|
- `chronicle observers remove` — destructive across every namespace of the event
|
|
193
201
|
store: definition, state, handled counts, failures and projection definition;
|
|
194
202
|
read model data stays. Refused while running or subscribed, no force flag.
|
|
195
203
|
- `chronicle observers clear-partition-quarantine` — may retry the handler
|
|
196
|
-
- `chronicle jobs stop`, `chronicle jobs resume`
|
|
204
|
+
- `chronicle jobs stop`, `chronicle jobs resume`, `chronicle jobs delete`
|
|
205
|
+
- `chronicle streams closed reopen` — exact manual scope repair with a reason
|
|
197
206
|
- `chronicle recommendations perform`, `chronicle recommendations ignore`
|
|
198
207
|
- `chronicle subscriptions add`, `chronicle subscriptions remove`
|
|
199
208
|
- `chronicle users add`, `chronicle users remove`
|
|
@@ -20,6 +20,7 @@ non-read-only commands.
|
|
|
20
20
|
| `workbench` | destructive | false (its actions have their own dialogs) |
|
|
21
21
|
| `applications add` | mutating | false |
|
|
22
22
|
| `applications remove` | destructive | true |
|
|
23
|
+
| `jobs delete` | destructive | true |
|
|
23
24
|
| `jobs resume` | mutating | true |
|
|
24
25
|
| `jobs stop` | mutating | true |
|
|
25
26
|
| `observers clear-partition-quarantine` | mutating | true |
|
|
@@ -27,14 +28,20 @@ non-read-only commands.
|
|
|
27
28
|
| `observers remove` | destructive | true |
|
|
28
29
|
| `observers replay` | destructive | true |
|
|
29
30
|
| `observers replay-partition` | destructive | true |
|
|
31
|
+
| `observers retry-failed-partitions` | mutating | true |
|
|
30
32
|
| `observers retry-partition` | mutating | true |
|
|
31
33
|
| `recommendations ignore` | mutating | true |
|
|
32
34
|
| `recommendations perform` | destructive | true |
|
|
35
|
+
| `streams closed reopen` | mutating | true |
|
|
33
36
|
| `subscriptions add` | mutating | false |
|
|
34
37
|
| `subscriptions remove` | destructive | true |
|
|
35
38
|
| `users add` | mutating | false |
|
|
36
39
|
| `users remove` | destructive | true |
|
|
37
40
|
|
|
41
|
+
See [recovery outcomes](recovery-and-comparison.md) for CLI/server floors and
|
|
42
|
+
acceptance versus completed work. `jobs delete` exits nonzero on declined/unavailable
|
|
43
|
+
confirmation; success means deletion requested, not completed removal.
|
|
44
|
+
|
|
38
45
|
The remaining Chronicle commands are cataloged read-only, including
|
|
39
46
|
`report-error`; the guard deliberately blocks that command because it opens an
|
|
40
47
|
external browser issue form. `chronicle version` is not a command in this
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
2
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
3
|
+
|
|
4
|
+
# CLI updates and project tooling
|
|
5
|
+
|
|
6
|
+
This complements live-store operations; it does not grant permission to install
|
|
7
|
+
servers, change a repository or run quota-consuming agent sessions. Inspect the
|
|
8
|
+
installed version with `cratis --version` and its catalog before using a flag.
|
|
9
|
+
|
|
10
|
+
## Inspect the upgrade, not a remembered bundle
|
|
11
|
+
|
|
12
|
+
Requires CLI 3.87.0 for offline embedded release notes:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
cratis release-notes --since <version> -o json
|
|
16
|
+
cratis release-notes --version <version>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`cratis update` displays the skipped releases after its update results. Requires
|
|
20
|
+
3.89.0 for `cratis new` to use the template set cached at `~/.cratis/templates`:
|
|
21
|
+
first use downloads from NuGet; offline use can reuse a complete cached set.
|
|
22
|
+
`cratis new cratis --language csharp --version <version>` selects a version for
|
|
23
|
+
one run rather than replacing the installed choice. `cratis update` refreshes
|
|
24
|
+
compatible templates without downgrading or failing the CLI update when refresh
|
|
25
|
+
fails; `--no-templates` skips it. CLI and template versions are separate. Do not
|
|
26
|
+
silently enable scaffold scripts; preserve the template's `--allow-scripts`
|
|
27
|
+
choice. Multi-service orchestration still needs explicit per-store settings.
|
|
28
|
+
|
|
29
|
+
For `cratis render` / `run`, read **cratis-screenplay-render-and-gap-fill** and
|
|
30
|
+
**cratis-stage-rendering-and-sandbox**. Their bundled renderer determines support;
|
|
31
|
+
a CLI upgrade can change admission, generated output and the default container
|
|
32
|
+
without changing the model. Never infer support from the ESM version alone or
|
|
33
|
+
edit generated output to make an older CLI accept it.
|
|
34
|
+
|
|
35
|
+
## Managed AI installation and MCP
|
|
36
|
+
|
|
37
|
+
Requires CLI 3.77.1 for `.pi/agents` to link to Pi-native corpus adapters when
|
|
38
|
+
available, falling back to canonical agents only for older corpora. Do not
|
|
39
|
+
hand-edit generated adapters to restore missing tools; update the CLI/corpus.
|
|
40
|
+
|
|
41
|
+
Requires 3.90.0 for profile-selected servers in the corpus's `mcp-servers.json` to
|
|
42
|
+
be registered by `cratis ai install` / `update` in Claude Code, Copilot, Cursor,
|
|
43
|
+
Codex and OpenCode. Installation registers configuration; it does not start the
|
|
44
|
+
servers. Deselecting/uninstalling removes only unchanged owned entries. Foreign
|
|
45
|
+
or modified entries remain conflicts, even with force. Resolve a conflict by
|
|
46
|
+
intentionally renaming/removing your entry or disabling that server with
|
|
47
|
+
`mcpServers.<id>.enabled = false` in the project configuration; do not overwrite
|
|
48
|
+
it speculatively. Pi supports only Screenplay through its native extension and
|
|
49
|
+
reports other servers unsupported. An invalid selected descriptor is reported
|
|
50
|
+
and skipped; successful guidance installation does not prove MCP availability.
|
|
51
|
+
Installing a server does not authorize its tools or commands.
|
|
52
|
+
|
|
53
|
+
## Deterministic AI checks versus harness behavior
|
|
54
|
+
|
|
55
|
+
Requires CLI 3.98.0 for opt-in `cratis ai test`. Each fixture × harness uses a
|
|
56
|
+
fresh temporary workspace with the fixture's selected profiles/languages; the
|
|
57
|
+
invoking repository is unchanged. It is **not** a security sandbox: fixture
|
|
58
|
+
commands and harnesses still have host tools/authentication. Trust the source,
|
|
59
|
+
request the intended session/budget, and keep transcripts off unless needed.
|
|
60
|
+
The default fixture root is `Evaluations/conformance`; `--fixtures` overrides
|
|
61
|
+
it, `--fixture` selects names, and `--harnesses claude,codex` narrows adapters.
|
|
62
|
+
`--output json` is this command's structured format, not proof of a passing run.
|
|
63
|
+
|
|
64
|
+
Requires 3.100.0 for install-only CI without a harness, credentials or model quota:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
cratis ai test --source <corpus> --install-only --output json
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This runs install-phase checks for declared harness ids (or the configured ids
|
|
71
|
+
when a fixture declares none), narrowed by `--harnesses`. Result-phase checks
|
|
72
|
+
stay `not_run`; no install checks means skipped, not behavioral conformance.
|
|
73
|
+
Normal mode rejects missing/modified installations unless corpus-source mode is
|
|
74
|
+
selected. `--source`/`CRATIS_AI_SOURCE` selects a trusted local corpus.
|
|
75
|
+
|
|
76
|
+
Fixtures require a non-optional result check even in install-only mode. File
|
|
77
|
+
checks choose one path/glob; an empty glob match fails. Install checks cannot be
|
|
78
|
+
commands. A result command uses `run: ["executable", "argument"]` with no implicit
|
|
79
|
+
shell and cannot also declare file path/glob/pattern fields. Invalid check shapes
|
|
80
|
+
are errors. Paths and links cannot escape the workspace.
|
|
81
|
+
|
|
82
|
+
A timeout kills the process tree; output is capped. Requested `--timeout` and
|
|
83
|
+
`--max-budget-usd` can lower, not raise, fixture limits. Claude enforces a spending
|
|
84
|
+
limit; Codex reports that bound `not_enforced`. Missing harnesses/credentials are
|
|
85
|
+
skipped with a reason, never passed. Exit 0 can mean all skipped or no selected
|
|
86
|
+
runs: inspect counts and `no fixtures ran successfully`. Exit 1 means fixture,
|
|
87
|
+
harness or check failures/errors; exit 2 means usage/install errors. Installed
|
|
88
|
+
file hashes, observed tool reads and check results are separate evidence levels;
|
|
89
|
+
unknown reads remain unknown. `--keep-transcripts` retains best-effort-redacted
|
|
90
|
+
temporary output; inspect it for secrets before sharing. Passing install checks
|
|
91
|
+
is not proof that an agent read or obeyed the guidance.
|
package/package/corpus/skills/cratis-chronicle-cli-operations/references/recovery-and-comparison.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
2
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
3
|
+
|
|
4
|
+
# Recovery outcomes and namespace comparison
|
|
5
|
+
|
|
6
|
+
Check the installed `cratis llm-context` and server version before using these
|
|
7
|
+
commands. Confirm server, event store, namespace and exact targets. A prompt or
|
|
8
|
+
`--yes` is not authority to mutate a live store.
|
|
9
|
+
|
|
10
|
+
## Jobs: acceptance is not completed removal
|
|
11
|
+
|
|
12
|
+
Requires CLI 3.79.0 for `chronicle jobs stop` / `resume` and the terminal
|
|
13
|
+
Workbench to report kernel refusals as failures, with reasons in text/JSON and a
|
|
14
|
+
nonzero exit. Do not parse an old CLI's success message as proof of progress.
|
|
15
|
+
Inspect the job again; resume can be refused for missing, concluded, unpersisted
|
|
16
|
+
or otherwise unresumable work. Do not repeat it without understanding the reason.
|
|
17
|
+
|
|
18
|
+
Requires CLI 3.99.0 for `cratis chronicle jobs delete <JOB_ID>`. It is destructive
|
|
19
|
+
and irreversible: retained job state is removed. Before confirmation it names
|
|
20
|
+
the store, namespace and job id. An invalid GUID, declined/unavailable prompt,
|
|
21
|
+
server refusal, authorization failure or server error is nonzero. `--yes` skips
|
|
22
|
+
only the local prompt; the server still authorizes and decides. Exit zero and
|
|
23
|
+
"deletion requested" are **not** proof of completed removal. Capture the relevant
|
|
24
|
+
pre-state first and inspect again after an authorized deletion. This is not a
|
|
25
|
+
terminal Workbench key or permission to delete every retained job.
|
|
26
|
+
|
|
27
|
+
## Retry all failed partitions
|
|
28
|
+
|
|
29
|
+
Requires CLI 3.79.0 and Chronicle 19.80.0 for
|
|
30
|
+
`cratis chronicle observers retry-failed-partitions <OBSERVER_ID>`. It starts
|
|
31
|
+
server-owned bounded-batch recovery and reports how many partitions were failed;
|
|
32
|
+
it does not wait for all handlers to succeed. Inspect failures first, fix their
|
|
33
|
+
cause, authorize the observer-wide target, then re-read state after the request.
|
|
34
|
+
Do not mistake this for a client loop of one-partition requests or a guarantee
|
|
35
|
+
that quarantines were cleared.
|
|
36
|
+
|
|
37
|
+
## Inspect or repair a closed scope
|
|
38
|
+
|
|
39
|
+
Requires CLI 3.80.0 and Chronicle 19.39.0 for
|
|
40
|
+
`cratis chronicle streams closed list`. It is read-only and lists exact scope
|
|
41
|
+
dimensions, origin, owner constraint, sequence number and closing time. Filter by
|
|
42
|
+
event source id/type and event stream type/id; do not select a repair from a
|
|
43
|
+
partial dimension match.
|
|
44
|
+
|
|
45
|
+
`cratis chronicle streams closed reopen --reason <text>` is a confirmed,
|
|
46
|
+
mutating repair of **one exact** mistaken manual close. Supply the scope flags
|
|
47
|
+
shown by the installed help/catalog and the reason. The kernel records actor
|
|
48
|
+
and reason in `StreamScopeReopened` and refuses a closure owned by a closing
|
|
49
|
+
event. `--yes` skips confirmation, not that ownership check. Never turn this into
|
|
50
|
+
a way around a modeled closure or reopen broad scopes speculatively.
|
|
51
|
+
|
|
52
|
+
## Compare a model with live metadata
|
|
53
|
+
|
|
54
|
+
Requires CLI 3.85.0 for
|
|
55
|
+
`cratis screenplay conform-namespace <model> --event-store <store> --namespace <ns>`.
|
|
56
|
+
This is a read-only comparison, not model authoring, rendering or state repair.
|
|
57
|
+
It compares registered event types, producing commands/reactors and observers
|
|
58
|
+
with the model, scanning metadata up to the tail captured at start. The default
|
|
59
|
+
`--max-events` is `100000`; `--from`/`--to` bound a range. A partial scan stays
|
|
60
|
+
partial; without `--fail-on-drift` a drift report need not fail the command.
|
|
61
|
+
|
|
62
|
+
Requires 3.89.1 for authorization failures to exit 4 (`authentication_error`),
|
|
63
|
+
Ctrl+C between pages to exit 8 (`inconclusive`), and event attribution only when
|
|
64
|
+
the causation is a command. Chronicle's metadata API still transmits event source
|
|
65
|
+
ids and subjects; the CLI drops them without persisting them. Do not claim they
|
|
66
|
+
never cross the connection. It does not request payloads or command values.
|
|
67
|
+
Redact the report before sharing it, and do not treat an incomplete or cancelled
|
|
68
|
+
comparison as evidence of conformance.
|
|
@@ -32,9 +32,11 @@ Chronicle extensions — Chronicle declares no `Observe` on `IMongoCollection<T>
|
|
|
32
32
|
|
|
33
33
|
## Discovery rules
|
|
34
34
|
|
|
35
|
-
Arc discovers eligible **public
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
Arc discovers eligible **public static** methods on `[ReadModel]` types.
|
|
36
|
+
Requires Arc 22.121.0: non-public methods are excluded even with `[Query]`, and
|
|
37
|
+
ARC0029 warns on a non-public query-shaped method. A public query on an internal
|
|
38
|
+
read-model type still works. On older Arc, internal statics are exposed too;
|
|
39
|
+
keep helpers outside the read model when supporting those versions. Use
|
|
38
40
|
`[ReadModel(RequireQueryAttribute = true)]` and `[Query]` on intended endpoints
|
|
39
41
|
when the type also has folds (`Create`/`Reduce`); `[Query]` alone does not hide
|
|
40
42
|
siblings. Requires Arc 22.27.1 for exclusion of private, protected, special-name
|
|
@@ -138,7 +138,11 @@ Several views bind a key to an operation that changes the running server:
|
|
|
138
138
|
| Jobs | `S` / `U` | stop / resume the job |
|
|
139
139
|
| Recommendations | `A` / `I` | perform / ignore the recommendation |
|
|
140
140
|
|
|
141
|
-
Each has a bulk form that applies to every checked row.
|
|
141
|
+
Each has a bulk form that applies to every checked row. Requires CLI 3.79.0 for
|
|
142
|
+
job stop/resume to display kernel refusals as failures rather than success.
|
|
143
|
+
Inspect the reason and re-read the job; acknowledgement is not completed work.
|
|
144
|
+
Retained-job deletion is a separate CLI command (3.99.0), not a Workbench key;
|
|
145
|
+
read **cratis-chronicle-cli-operations** before requesting it.
|
|
142
146
|
|
|
143
147
|
Every one of these opens a centered confirmation modal that states the action
|
|
144
148
|
cannot be undone. **Only an explicit `Y` (or the Confirm button) confirms;
|
|
@@ -11,32 +11,22 @@ license: MIT
|
|
|
11
11
|
|
|
12
12
|
For services and kernel contributors using Orleans. This is library-consumer guidance, not the application vertical-slice architecture. For live Chronicle operations use **cratis-chronicle-cli-operations** instead. Co-hosting conventions are in [orleans.md](../../rules/orleans.md).
|
|
13
13
|
|
|
14
|
-
## Verified product sources
|
|
15
|
-
|
|
16
|
-
All Orleans claims below come from `Cratis/Orleans` source.
|
|
17
|
-
|
|
18
|
-
| Packages | Sources |
|
|
19
|
-
| --- | --- |
|
|
20
|
-
| `Cratis.Orleans`, `Cratis.Orleans.Storage` | `Source/Orleans/Jobs/{Job.cs,Job.Steps.cs,Job.Stages.cs,JobStep.cs,IJobsManager.cs,JobsOptions.cs}`; `Source/Orleans.Storage/Jobs/*` |
|
|
21
|
-
| `Cratis.Orleans.Storage.MongoDB`, `Cratis.Orleans.Storage.Sql` | `Source/Orleans.Storage.MongoDB/{MongoDBJobsStorageOptions.cs,DatabaseNames.cs}`; `Source/Orleans.Storage.Sql/{SqlJobsStorage.cs,SqlJobsStorageOptions.cs,Jobs/Migrations/v1_0_0.cs}` |
|
|
22
|
-
| `Cratis.Orleans.Chronicle` | `Source/Orleans.Chronicle/StateMachines/EventSourcedStateMachine.cs`; [state-machine reference](references/state-machines.md) |
|
|
23
|
-
|
|
24
|
-
The API snippets cite their exact files and lines below. Product docs `Documentation/concepts/{jobs,storage-scopes,state-machines,event-sourced-state-machines}.md` supply the task vocabulary; source resolves contradictions.
|
|
25
|
-
|
|
26
14
|
## 1. Choose a job, not a detached task
|
|
27
15
|
|
|
28
|
-
A job plans recoverable work; each step is a grain and does the work. Use it for work that must outlive a request. It is **at-least-once**, not exactly-once: a step interrupted before a durable checkpoint can perform the same work again. Protect external effects with receiver-owned idempotency/deduplication; a checkpoint does not atomically commit an external write
|
|
16
|
+
A job plans recoverable work; each step is a grain and does the work. Use it for work that must outlive a request. It is **at-least-once**, not exactly-once: a step interrupted before a durable checkpoint can perform the same work again. Protect external effects with receiver-owned idempotency/deduplication; a checkpoint does not atomically commit an external write.
|
|
17
|
+
|
|
18
|
+
- Job: derive `Job<TRequest, TJobState>`; the request is a class implementing `IJobRequest`, the state derives `JobState`. Implement `PrepareSteps(TRequest)` returning `Task<IImmutableList<JobStepDetails>>`.
|
|
19
|
+
- Step: derive `JobStep<TRequest, TResult, TState>`; state derives `JobStepState`. Its constructor receives `[PersistentState(nameof(JobStepState), WellKnownGrainStorageProviders.JobSteps)] IPersistentState<TState>`, `IJobStepThrottle`, and `ILogger<JobStep<TRequest, TResult, TState>>`.
|
|
20
|
+
- Implement `PrepareStep(TRequest)` → `Task<Result<PrepareJobStepError>>`; `InitializeState(TRequest)` → `ValueTask`, called after preparation succeeds; and `PerformStep(TState, CancellationToken)` → `Task<Catch<JobStepResult>>`. `TResult` is the step's result type; the work method returns the framework's `JobStepResult` envelope, not `TResult` directly.
|
|
21
|
+
- Give the concrete job and step unique `[GrainType("product-specific-name")]` names. Use serializable request/state/result types. Do not reuse a class-derived grain name across assemblies.
|
|
29
22
|
|
|
30
|
-
-
|
|
31
|
-
- Step: derive `JobStep<TRequest, TResult, TState>`; state derives `JobStepState`. Its constructor receives `[PersistentState(nameof(JobStepState), WellKnownGrainStorageProviders.JobSteps)] IPersistentState<TState>`, `IJobStepThrottle`, and `ILogger<JobStep<TRequest, TResult, TState>>` (`JobStep.cs`).
|
|
32
|
-
- Implement `PrepareStep(TRequest)` → `Task<Result<PrepareJobStepError>>`; `InitializeState(TRequest)` → `ValueTask`, called after preparation succeeds; and `PerformStep(TState, CancellationToken)` → `Task<Catch<JobStepResult>>` (`JobStep.cs`). `TResult` is the step's result type; the work method returns the framework's `JobStepResult` envelope, not `TResult` directly.
|
|
33
|
-
- Give the concrete job and step unique `[GrainType("product-specific-name")]` names. Use serializable request/state/result types. Do not reuse a class-derived grain name across assemblies (`Jobs/JobsManager.cs`, `Documentation/how-to/host-the-silo.md`, and `orleans.md`).
|
|
23
|
+
`PerformStep` runs **outside the activation context** with a deep-copied state snapshot. Mutating that snapshot does not persist it; do not access/write grain `State` directly from background work. A specification host such as Orleans.TestKit must register `DeepCopier<TState>`.
|
|
34
24
|
|
|
35
|
-
`
|
|
25
|
+
Requires Cratis.Orleans 1.19.0 for `TryInvokeOnActivation` to coordinate state/checkpoints on the current activation, including during deactivation. Call it only from the run's background work. Keep callbacks local: capture copied inputs, call remote systems outside the callback, then commit in a second callback. Never call another grain, nest and await another callback, or start unawaited work inside it. Inspect `ActivationCallbackRejected` (`NoRun`, `StaleRun`, `Closed`, `Cancelled`); exceptions propagate. The optional `queueCancellationToken` cancels waiting callbacks only; started callbacks finish. Stop/deactivation waits for started callbacks and work to settle; `JobsOptions.StepStopTimeout` defaults to 10 seconds and `JobStepError.StopTimedOut` is not proof of quiescence. Requires 1.16.2 for framework-owned stop reporting: do not report cancellation again from a `PerformWork` override.
|
|
36
26
|
|
|
37
27
|
## 2. Plan dependencies as stages
|
|
38
28
|
|
|
39
|
-
Default steps all use `JobStepStage.First`. Prefer independent fan-out. Use stages only when correctness requires earlier work to finish
|
|
29
|
+
Default steps all use `JobStepStage.First`. Prefer independent fan-out. Use stages only when correctness requires earlier work to finish.
|
|
40
30
|
|
|
41
31
|
Planning excerpt inside an existing `Job<RecoveryRequest, RecoveryState>`. It assumes `RecoveryRequest : IJobRequest`, `RecoveryState : JobState`, and the two existing grain interfaces `IRepairIndex` and `IRebuildCatalog`, each deriving a typed `IJobStep<,,>`:
|
|
42
32
|
|
|
@@ -52,64 +42,62 @@ protected override Task<IImmutableList<JobStepDetails>> PrepareSteps(RecoveryReq
|
|
|
52
42
|
]);
|
|
53
43
|
```
|
|
54
44
|
|
|
55
|
-
Import `System.Collections.Immutable` and `Cratis.Orleans.Jobs` in that job file. `CreateStep<TStep>` takes the **grain interface**, not the implementation
|
|
45
|
+
Import `System.Collections.Immutable` and `Cratis.Orleans.Jobs` in that job file. `CreateStep<TStep>` takes the **grain interface**, not the implementation. Its stage overload accepts `JobStepStage`, with implicit integer conversion.
|
|
56
46
|
|
|
57
|
-
Every step in a stage reaches an outcome before later stages start; steps within a stage run in parallel. All steps are prepared up front, so later-stage preparation cannot read the effects of earlier-stage execution. Read such dependencies in `PerformStep
|
|
47
|
+
Every step in a stage reaches an outcome before later stages start; steps within a stage run in parallel. All steps are prepared up front, so later-stage preparation cannot read the effects of earlier-stage execution. Read such dependencies in `PerformStep`.
|
|
58
48
|
|
|
59
|
-
A failed stage stops the pipeline by default: later steps become `Unreachable`, and the job completes with failures. A best-effort stage may override `GetFailureBehaviorFor(JobStepStage)` to return `JobStageFailureBehavior.ContinueWithNextStage` (
|
|
49
|
+
A failed stage stops the pipeline by default: later steps become `Unreachable`, and the job completes with failures. A best-effort stage may override `GetFailureBehaviorFor(JobStepStage)` to return `JobStageFailureBehavior.ContinueWithNextStage` (namespace `Cratis.Orleans.Jobs.Stages`). Do not silently continue when a later stage relies on the failed output.
|
|
60
50
|
|
|
61
|
-
|
|
51
|
+
Requires 1.25.0 for durable step plans: preparation requests must round-trip through the host's JSON options. Invalid plans return `StartJobError.InvalidStepPlan`. Recovery reuses identities and requests, prepares only unsaved steps, and never calls `PrepareSteps` again. Put retry-safe initialization needed before start **and** resume in `OnBeforeDispatchingJobSteps(JobStepDispatchContext)`, after the respective start/resume hook. `InitializationMayBeIncomplete` flags recovery from preparing/starting, not exactly-once initialization. A failed hook dispatches nothing; recovery never replays the start hook. A legacy preparing/starting job without a saved plan returns `CannotResumeJobError.MissingStepPlan` rather than inventing identities.
|
|
62
52
|
|
|
63
|
-
|
|
53
|
+
## 3. Start locally and recover every scope
|
|
64
54
|
|
|
65
|
-
|
|
55
|
+
Requires 1.18.0 for the singleton local service `Cratis.Orleans.Jobs.IJobs`, registered by `AddCratisOrleans`. Prefer `Start<TJob,TRequest>(new JobKey(scope, namespace), request)` (or its explicit job-id overload), `Stop(key, jobId)` and `Delete(key, jobId)` during rolling upgrades. Generic `IJobsManager.Start`/`GetJobsOfType` send types through a manager that may run an older release and cannot decode them.
|
|
66
56
|
|
|
67
|
-
`
|
|
57
|
+
Inspect `Result<JobId, StartJobError>` before telling a caller that work started. `FailedStartingSomeJobSteps` means remaining work is still running, not that nothing started; `AllJobStepsFailedStarting` means no step started. Start success is not completion. Match `StopJobResult`/`DeleteJobResult`: accepted, `NotFound`, `UnknownJobType`, or `Refused` with the job's error. Unknown types require a release that knows the type; exceptions propagate. Deletion acceptance is not completed removal. Third-party storage should override `IJobStorage.GetJobType` with a metadata-only read; its fallback reads the request too.
|
|
68
58
|
|
|
69
|
-
|
|
59
|
+
`IGrainFactory.GetJobsManager(scope, namespace)` remains available. `IJobsManager.Resume(JobId)` returns **`Task<bool>`**; false means nothing took the job: do not wait for a completion callback as though resumption succeeded. True may also mean already running or finalized. `IJobsManager.Stop` returns `Result<StopJobError>`; preserve a persistence refusal. `Rehydrate()` returns after loading/preparing interrupted jobs, before its bounded background drain resumes them all. `StartStepsInBackground = true` moves step preparation/start to the next turn after durability; later failures belong to the job, not Start.
|
|
70
60
|
|
|
71
|
-
|
|
61
|
+
Requires 1.17.0 for automatic recovery on silo activation/member death. Provide singleton `IJobsManagerKeys` implementations on **every silo**, enumerating all host-owned scope/namespace pairs through `GetKeys(CancellationToken)`. The default remembers only keys used in this process: it is not a durable catalog. Providers run outside request scope; never inject a current tenant/user or scoped database. Requires 1.23.3 (1.24.2 for provider construction failures) for automatic recovery retries of failed providers/managers, from 1 second doubling to 60 seconds until success or silo shutdown. There is no periodic sweep by default. `AddCratisOrleans` registers recovery; separately configured silos need `AddCratisOrleansJobsRecovery()` (`Cratis.Orleans.Hosting`).
|
|
72
62
|
|
|
73
|
-
|
|
63
|
+
Read [lifecycle and placement](references/lifecycle-and-placement.md) for drain/admission, bounded attempts, placement, custom storage compatibility and step test runs.
|
|
74
64
|
|
|
75
|
-
|
|
65
|
+
## 4. Know what storage retains
|
|
76
66
|
|
|
77
|
-
|
|
67
|
+
Successful jobs clear themselves by default; override `KeepAfterCompleted` to retain state. Jobs completed with failures default to `KeepAfterCompletedWithFailures = true`, but retention cleanup still applies.
|
|
68
|
+
|
|
69
|
+
Keeping a job does not keep successful step state: successful **steps delete their state**. Failed step state is retained. A durable callback receipt, deduplication set or result must live on the **job** (and be persisted), not only on a successful step. Deliver external callbacks to a method on the job's grain interface and preserve `WriteState()`'s `Catch` failure: do not acknowledge a failed write. Step state is not a callback inbox, and a running snapshot does not see later writes automatically.
|
|
78
70
|
|
|
79
|
-
|
|
71
|
+
## 5. Register storage and budget concurrency
|
|
80
72
|
|
|
81
|
-
Choose one storage registration alongside `AddCratisOrleans`:
|
|
73
|
+
`IJobsStorage.GetFor(scope, namespace)` returns `JobsStorage`. Scope and namespace are opaque host-owned storage coordinates, not authorization checks. Choose one storage registration alongside `AddCratisOrleans`:
|
|
82
74
|
|
|
83
75
|
| Registration | Package / namespace | Use |
|
|
84
76
|
| --- | --- | --- |
|
|
85
|
-
| `AddCratisOrleansMongoDBJobsStorage` | `.Storage.MongoDB` / `Cratis.Orleans.Setup` | Durable MongoDB; database
|
|
86
|
-
| `AddCratisOrleansSqlJobsStorage` | `.Storage.Sql` / `Cratis.Orleans.Setup` | Durable EF Core; `OptionsResolver(scope, namespace)` selects
|
|
87
|
-
| `AddCratisOrleansInMemoryJobsStorage` | `.Storage` / `Cratis.Orleans.Storage` | Specs/development
|
|
88
|
-
|
|
89
|
-
Evidence: each package's `*JobsStorageServiceCollectionExtensions.cs`; MongoDB `DatabaseNames.cs` and `MongoDBJobsStorageOptions.cs`; SQL `SqlJobsStorageOptions.cs`; in-memory `InMemory/InMemoryJobsStorage.cs`.
|
|
90
|
-
|
|
91
|
-
### SQL schema creation
|
|
77
|
+
| `AddCratisOrleansMongoDBJobsStorage` | `.Storage.MongoDB` / `Cratis.Orleans.Setup` | Durable MongoDB; default database `{scope}+jobs[+{namespace}]`, overridable |
|
|
78
|
+
| `AddCratisOrleansSqlJobsStorage` | `.Storage.Sql` / `Cratis.Orleans.Setup` | Durable EF Core; `OptionsResolver(scope, namespace)` selects `DbContextOptions<JobsDbContext>` |
|
|
79
|
+
| `AddCratisOrleansInMemoryJobsStorage` | `.Storage` / `Cratis.Orleans.Storage` | Specs/development; restart loses state |
|
|
92
80
|
|
|
93
|
-
The
|
|
81
|
+
The SQL provider calls `dbContext.Database.Migrate()` automatically before constructing storage. Do not substitute `EnsureCreated` or hand-create tables; grant migration permissions and choose scope-to-database mapping deliberately. `IJobsStorage.Reset()` clears the cache; the next resolution migrates again. There is no MySQL/MariaDB jobs provider.
|
|
94
82
|
|
|
95
|
-
|
|
83
|
+
`JobsOptions` properties are **init-only**: supply a constructed instance through the host's options mechanism, not assignment in `Configure(options => …)`.
|
|
96
84
|
|
|
97
85
|
| Option | Default | Purpose |
|
|
98
86
|
| --- | --- | --- |
|
|
99
|
-
| `MaxParallelSteps` | `max(1, ProcessorCount - 1)` when unset |
|
|
100
|
-
| `MaxConcurrentStepStarts` | `16` | Startup/resume calls per job, not
|
|
87
|
+
| `MaxParallelSteps` | `max(1, ProcessorCount - 1)` when unset | Execution throttle per silo |
|
|
88
|
+
| `MaxConcurrentStepStarts` | `16` | Startup/resume calls per job, not execution limit |
|
|
101
89
|
| `MaxConcurrentRehydration` | `8` | Jobs resumed concurrently by a manager |
|
|
102
90
|
| `MaxConcurrentCleanup` | `4` | Dead-job deletions |
|
|
103
|
-
| `StepCheckpointBatchInterval` | `100` | Batches between durable
|
|
104
|
-
| `StepCheckpointFlushInterval` | `5 seconds` | Timed
|
|
105
|
-
| `DeadJobThreshold`, `CleanupCadence` | `1 hour` each | Preparation-with-no-steps scavenging |
|
|
106
|
-
| `TerminalJobRetention` | `7 days` | Terminal history
|
|
91
|
+
| `StepCheckpointBatchInterval` | `100` | Batches between durable checkpoints |
|
|
92
|
+
| `StepCheckpointFlushInterval` | `5 seconds` | Timed flush; nonpositive disables it |
|
|
93
|
+
| `DeadJobThreshold`, `CleanupCadence` | `1 hour` each | Preparation-with-no-steps scavenging; saved plans are excluded |
|
|
94
|
+
| `TerminalJobRetention` | `7 days` | Terminal history; `Timeout.InfiniteTimeSpan` retains indefinitely |
|
|
107
95
|
| `MaxTerminalJobsPerCleanup` | `100` | Bounded oldest-first cleanup page |
|
|
108
96
|
|
|
109
|
-
|
|
97
|
+
Concurrency values below one become one. Retention does not delete preparing/running/stopped/removing jobs. Larger checkpoint intervals increase post-crash redelivery, not delivery guarantees.
|
|
110
98
|
|
|
111
99
|
## State machines and verification
|
|
112
100
|
|
|
113
|
-
Use [state-machines.md](references/state-machines.md) for `StateMachine<TStoredState>` and `EventSourcedStateMachine<TModel
|
|
101
|
+
Use [state-machines.md](references/state-machines.md) for `StateMachine<TStoredState>` and `EventSourcedStateMachine<TModel>`. Neither makes external effects exactly-once.
|
|
114
102
|
|
|
115
|
-
Verify
|
|
103
|
+
Verify planning, invalid/missing saved plans, idempotent redelivery, stop/resume/refusal, failed barriers/Unreachable, retention and persistence failures. Exercise durable storage restart recovery and SQL migration permissions. A build alone cannot establish crash recovery. Do not operate a live store merely to validate an example.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
2
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
3
|
+
|
|
4
|
+
# Lifecycle, recovery and placement
|
|
5
|
+
|
|
6
|
+
## Stop, drain and admission
|
|
7
|
+
|
|
8
|
+
Requires Cratis.Orleans 1.20.0 for `IJobs.StopAndDrain(key, jobId, timeout)` and the grain protocols `IJobDrain` / `IJobStepDrain`. Unlike ordinary stop acceptance, `Drained` means no step code can run, final outcomes/checkpoints are durable and completion/stop hooks finished. Other outcomes are `TimedOut` (call again), `Completed`, `NotFound` and `UnknownJobType`. `TimeSpan.Zero` polls. Add the method to hand-written `IJobs` implementations. Enable only after every silo supports it. A pending drain survives reactivation and refuses resume; a timeout is not proof of quiescence. Hooks can repeat or be missed after a crash, so keep them idempotent rather than collecting results only there.
|
|
9
|
+
|
|
10
|
+
Requires 1.21.0 for owner admission: implement `IJobAdmission.MayRun(JobAdmissionRequest, CancellationToken)` and register with `siloBuilder.AddJobAdmission<T>()` on every silo that hosts those job types. Every admission must allow; none means allow. It runs before start, every resume and each later stage. Refusals return `StartJobError.NotAdmitted` / `CannotResumeJobError.NotAdmitted`; manager resume returns false. A refused running job is stopped/drained, and a refused later stage stops the job. Exceptions/timeouts are failures, not refusals; a later stage retries an unanswered admission on a timer.
|
|
11
|
+
|
|
12
|
+
`MayRun` runs in the job's turn: never call the job, its steps, or a non-interleaving grain that could be waiting on this start/resume. To change what existing work may do, the owner calls `StopAndDrain` **before** changing the admission answer. Older silos do not ask, so admission guarantees require every silo to run the supporting version. Admission does not by itself serialize arbitrary host-owned mutual-exclusion rules.
|
|
13
|
+
|
|
14
|
+
## Bounded delivery and retries
|
|
15
|
+
|
|
16
|
+
Requires 1.23.0 for these opt-in execution controls. Defaults preserve unbounded dispatch, one execution attempt and no lease timeout:
|
|
17
|
+
|
|
18
|
+
| Option | Default / meaning |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| `MaxActiveStepsPerJob` | `null` (all); a positive limit fills a dispatch window within a stage |
|
|
21
|
+
| `MaxStepAttempts` | `1`, including first attempt; failed work can retry when increased |
|
|
22
|
+
| `StepRetryBackoff`, `MaxStepRetryBackoff` | `5 seconds`, `5 minutes`; capped exponential delay |
|
|
23
|
+
| `JobWatchdogInterval` | `1 minute`; probes stalled delivery/progress |
|
|
24
|
+
| `StepLeaseTimeout` | `TimeSpan.Zero` disables heartbeat lease enforcement |
|
|
25
|
+
| `StepDeliveryMaxAttempts` | `10` |
|
|
26
|
+
| `StepDeliveryRetryDelay`, `MaxStepDeliveryRetryDelay` | `1 second`, `30 seconds` |
|
|
27
|
+
|
|
28
|
+
The watchdog restarts a leased step only after its owner silo left the cluster **and** its lease expired. Steps saved by older releases get one lease period. `Job.ShouldRetryStep` decides which failures retry, and `Job.MaxActiveSteps` can override the window. Persist optional `Attempt` and `Lease` on custom step state; old records default to attempt 1 and no lease. `IJobStepStorage.CountByStatus(jobId)` has a default implementation. A step persists its terminal state before retrying outcome delivery; exhausted delivery does not turn that completed step into a new failed execution. The watchdog reconciles stored outcomes if reports never arrive. Keep work idempotent: leases and delivery deduplication do not make external effects exactly-once.
|
|
29
|
+
|
|
30
|
+
`IJobsManager.RetryFailedSteps(jobId)` is opt-in through `Job.CanRetryFailedSteps` and runs failed steps of a job completed with failures again. Ordinary resume is unchanged. It is not a general rerun of a successful job.
|
|
31
|
+
|
|
32
|
+
## Step test runs
|
|
33
|
+
|
|
34
|
+
Requires 1.22.0 for `step.BeginTestRun()` and `JobStepTestRun<TState>.Perform(state)` in a specification host. `Perform` runs `PerformStep` off activation with the supplied state and returns its `Catch<JobStepResult>`; `TryInvokeOnActivation` callbacks run serially on a stand-in scheduler. `IsOnActivation` tests that state changes happen in callbacks. `Stop()` cancels work while admission remains open; `CloseAdmission()` refuses callbacks. A run performs once and records/delivers nothing to a job. Cover `JobStepAlreadyHasALiveRun` and `JobStepTestRunWasSuperseded` misuse. This does not isolate external effects: use safe substitutes, not a live job/store.
|
|
35
|
+
|
|
36
|
+
## Custom storage and rolling upgrades
|
|
37
|
+
|
|
38
|
+
Persist the complete `JobState`, including `StopFinalized`, `StoppedStepsCounted` and `CompletedStepsCounted`. Missing lists read as empty for old records; do not project these fields away when storing state. The completion list tracks outcomes in executions begun by resume/retry, not jobs never resumed, and warns at 100,000 entries. Cross-version code that does not persist the list can lose duplicate protection, so keep mixed-version windows short.
|
|
39
|
+
|
|
40
|
+
A completed stop sets `StopFinalized` after all required notifications have succeeded. Unfinalized stops are retried; finalized stops are excluded from recovery through `IJobStorage.GetJobsExcludingFinalizedStops`. Custom providers can override its default in-memory filtering to filter in storage. MongoDB and SQL filter there; MongoDB adds `status_stopFinalized`. SQLite, PostgreSQL and SQL Server jobs storage are supported; MySQL/MariaDB is not. Removing jobs likewise retries unacknowledged notifications; acceptance of removal is not quiescence or immediate physical deletion.
|
|
41
|
+
|
|
42
|
+
## Independent controller and worker placement
|
|
43
|
+
|
|
44
|
+
Requires 1.30.0 for `Cratis.Orleans.Hosting` extensions on `ISiloBuilder`:
|
|
45
|
+
|
|
46
|
+
- `AddJobPlacement()` or `AddJobPlacement<TDirector>()` opts controllers into `JobsPlacementStrategy`.
|
|
47
|
+
- `AddJobStepPlacement()` or `AddJobStepPlacement<TDirector>()` opts workers into `JobStepsPlacementStrategy`, including recovered steps.
|
|
48
|
+
- Directors implement Orleans `IPlacementDirector`. Host directors decide eligibility; throw `NoEligibleJobWorker` when no silo is eligible. Orleans does not retry that exception; the job's failure handling applies.
|
|
49
|
+
- Without an opt-in, controller placement remains resource-optimized and workers use the cluster default. The built-in directors preserve those defaults when an opt-in has no custom director.
|
|
50
|
+
- **Wait until every silo runs the supporting version**, then register each strategy identically on every silo. Do not introduce the strategy during a mixed-version rollout.
|
|
51
|
+
|
|
52
|
+
`UseJobStepParallelismFromSiloMetadata()` independently sizes each silo's throttle from the positive integer `cratis.jobs.max-parallel-steps` metadata value (or the supplied metadata key). Missing, nonnumeric or nonpositive values fall back to `JobsOptions.MaxParallelSteps` and its processor-count default. Publish with Orleans `UseSiloMetadata`. This is a startup value, not live resizing, authorization or admission. Keep controller placement, worker eligibility, execution throttling and job admission as separate choices.
|
|
@@ -44,7 +44,9 @@ public class Finished : State<ProcessState>;
|
|
|
44
44
|
|
|
45
45
|
Split types into their house-style files in the consumer; the block collects declarations to show the API. Do not copy the `processes` provider name into Chronicle's kernel; use its registered constants.
|
|
46
46
|
|
|
47
|
-
A refused transition is a **no-op**, not an exception or a successful change. An unknown target throws `UnknownStateTypeInStateMachine`. The machine runs leaving hooks, `OnLeave`, entering hooks, `OnEnter`, then persistence (`StateMachine.cs`).
|
|
47
|
+
A refused transition is a **no-op**, not an exception or a successful change. An unknown target throws `UnknownStateTypeInStateMachine`. The machine runs leaving hooks, `OnLeave`, entering hooks, `OnEnter`, then persistence (`StateMachine.cs`).
|
|
48
|
+
|
|
49
|
+
Requires 1.22.3 for transitions admitted during entry to queue FIFO rather than be dropped. Their request tasks complete on admission, **before** the queued transition executes. If another state has been entered, the target is rechecked and silently skipped when refused; a failed drain discards pending targets and fails its owning task. Transitions while leaving still throw `TransitioningDuringOnLeaveIsNotSupported`.
|
|
48
50
|
|
|
49
51
|
When stored state derives `StateMachineState`, `CurrentState` stores the state's full type name; activation resolves it, enters it again, and can repeat enter effects. Renames need a stored-value migration; an unknown recorded name throws `UnknownCurrentState` (`StateMachine.cs`). A self-transition with the **same state instance** skips persistence: return a new stored-state instance when a self-transition must write.
|
|
50
52
|
|