@cratis/pi 7.123.0 → 7.125.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/agents/spec-writer.md +6 -6
- package/package/corpus/harnesses/copilot/instructions/application-profile.instructions.md +1 -1
- package/package/corpus/harnesses/copilot/instructions/framework.instructions.md +1 -0
- package/package/corpus/harnesses/copilot/instructions/specs.csharp.instructions.md +2 -2
- package/package/corpus/harnesses/copilot/instructions/specs.instructions.md +1 -1
- package/package/corpus/harnesses/copilot/instructions/specs.scenarios.csharp.instructions.md +16 -20
- package/package/corpus/harnesses/copilot/instructions/verification-discipline.instructions.md +1 -0
- package/package/corpus/harnesses/copilot/instructions/vertical-slices.instructions.md +5 -3
- package/package/corpus/harnesses/pi/agents/spec-writer.md +6 -6
- package/package/corpus/prompts/add-business-rule.prompt.md +2 -2
- package/package/corpus/rules/application-profile.md +1 -1
- package/package/corpus/rules/framework.md +1 -0
- package/package/corpus/rules/general.md +2 -1
- package/package/corpus/rules/specs.csharp.md +2 -2
- package/package/corpus/rules/specs.md +1 -1
- package/package/corpus/rules/specs.scenarios.csharp.md +16 -20
- package/package/corpus/rules/verification-discipline.md +1 -0
- package/package/corpus/rules/vertical-slices.md +5 -3
- package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +32 -21
- package/package/corpus/skills/cratis-application-slice-specifications/references/query-and-decision-scenarios.md +46 -44
- package/package/corpus/skills/cratis-arc-command/SKILL.md +5 -0
- package/package/corpus/skills/cratis-arc-command/references/decisions-and-routing.md +22 -14
- package/package/corpus/skills/cratis-arc-command/references/fresh-identifiers.md +13 -15
- package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +18 -12
- package/package/corpus/skills/cratis-arc-command-validation/references/protected-validator-construction.md +9 -5
- package/package/corpus/skills/cratis-chronicle-decision-reads/SKILL.md +12 -8
- package/package/corpus/skills/cratis-chronicle-decision-reads/references/admission-and-ownership.md +6 -2
- package/package/corpus/skills/cratis-chronicle-decision-reads/references/choosing-how-a-rule-holds.md +23 -30
- package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +5 -4
- package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +14 -3
- package/package/corpus/skills/cratis-chronicle-read-model-specifications/references/view-lifecycle-checks.md +6 -3
- package/package/corpus/skills/cratis-ci-pipeline-performance/SKILL.md +8 -4
- package/package/corpus/skills/cratis-ci-pipeline-performance/references/test-setup-cost.md +15 -0
- package/package/corpus/skills/cratis-code-review/SKILL.md +10 -7
- package/package/corpus/skills/cratis-screenplay-command-surface/references/generated-values-and-responses.md +5 -1
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/chronicle-verification.md +5 -3
- package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +19 -14
- package/package/corpus/skills/cratis-specifications-csharp/references/application-scenarios.md +20 -19
- package/package/corpus/skills/cratis-specifications-csharp/references/running-specs.md +32 -1
- package/package/corpus/skills/cratis-specifications-csharp/references/shared-lifecycle.md +32 -13
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +1 -1
- package/package.json +1 -1
|
@@ -10,8 +10,10 @@ metadata:
|
|
|
10
10
|
|
|
11
11
|
Start a pure decision with a plain method call: `Handle()`,
|
|
12
12
|
`Handle(providedValue)`, reducer logic or a reactor's decision needs no host,
|
|
13
|
-
services or harness
|
|
14
|
-
|
|
13
|
+
services or harness, including direct validator calls with supplied read-model state.
|
|
14
|
+
Add scenarios only for pipeline/authorization wiring, constraints, declarative
|
|
15
|
+
mapping; follow the
|
|
16
|
+
[scenario selection and cost rule](../../rules/specs.scenarios.csharp.md). The scenario family
|
|
15
17
|
exercises the real Arc and Chronicle code paths in-process — no HTTP, no fixture,
|
|
16
18
|
no server.
|
|
17
19
|
|
|
@@ -67,7 +69,8 @@ repository before claiming support for another version.
|
|
|
67
69
|
|
|
68
70
|
When the slice has a contract — a `.play` slice with its `specification`
|
|
69
71
|
blocks, or an agreed outline — its specifications are the **minimum list**: each
|
|
70
|
-
|
|
72
|
+
gets concrete assertions. Decision outcomes use focused classes; declarative
|
|
73
|
+
read-model mappings share one class per model with many read-only facts. Steps 3–7 then add what
|
|
71
74
|
the code reveals (each validator rule, each constraint). A rule found in the code
|
|
72
75
|
with no contract specification is a proposal for the contract (the `.play` model when one covers the slice, otherwise the agreed outline), not silent coverage:
|
|
73
76
|
list it in the report and, where a model exists, route it to
|
|
@@ -106,15 +109,16 @@ A behavior covering several subjects groups under
|
|
|
106
109
|
|
|
107
110
|
Specify a pure decision with the plain `Specification` base first. A direct
|
|
108
111
|
`Handle()` call does not run validation, authorization, `Provide()` or operations;
|
|
109
|
-
|
|
112
|
+
add a scenario only where that wiring adds proof. Keep shared lifecycle enabled
|
|
113
|
+
and measure scenario cost as described in the linked rule.
|
|
110
114
|
|
|
111
115
|
| Slice type | Scenario | Skill |
|
|
112
116
|
| --- | --- | --- |
|
|
113
|
-
| Pure command decision, reducer logic or reactor decision | Plain method call with `Specification` (default) | `cratis-specifications-csharp` |
|
|
117
|
+
| Pure command/validator decision, reducer logic or reactor decision | Plain method call with `Specification` and supplied read-model state (default) | `cratis-specifications-csharp` |
|
|
114
118
|
| State Change — command pipeline and appended events | `CommandScenario<TCommand>` | this one |
|
|
115
119
|
| Append semantics, a constraint | `EventScenario` | `cratis-chronicle-event-specifications` |
|
|
116
120
|
| Query pipeline — arguments, authorization, validation, interceptors | `QueryScenario<TReadModel>` | [query and decision scenarios](references/query-and-decision-scenarios.md) |
|
|
117
|
-
| State View —
|
|
121
|
+
| State View — declarative mapping or reducer wiring | One `ReadModelScenario<TReadModel>` class per read model, many assertions on shared setup | `cratis-chronicle-read-model-specifications` |
|
|
118
122
|
| Automation or Translation — a reactor | `ReactorScenario<TReactor>` | `cratis-specifications-csharp` |
|
|
119
123
|
| A host, transport, or real-infrastructure boundary | Out-of-process integration | `cratis-specifications-csharp` |
|
|
120
124
|
|
|
@@ -131,9 +135,11 @@ exception diagnostics, synchronous handler throws and UTC context defaults.
|
|
|
131
135
|
|
|
132
136
|
One specification class for **each** of:
|
|
133
137
|
|
|
134
|
-
1. **The happy path** —
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
1. **The happy path** — a plain call returns the expected event; add pipeline
|
|
139
|
+
coverage only for behavior that call cannot prove.
|
|
140
|
+
2. **Each validation failure** — instantiate `CommandValidator` or
|
|
141
|
+
`ConceptValidator` with the required state and validate directly. Use a
|
|
142
|
+
scenario only to prove pipeline wiring.
|
|
137
143
|
3. **Each business-rule rejection** — one `and_` class per condition in
|
|
138
144
|
`Handle()` that inspects a read model.
|
|
139
145
|
4. **Each constraint violation** — one class per constraint, written with
|
|
@@ -194,24 +200,27 @@ _scenario.Given.ForEventSource(_cartId).Events(new ItemAddedToCart(itemId));
|
|
|
194
200
|
_scenario.Given.ForEventSource(_cartId).ReadModel(new CartSummary { ItemCount = 3 });
|
|
195
201
|
```
|
|
196
202
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
203
|
+
Supply read-model state for a business decision. Seed events only when the
|
|
204
|
+
history itself matters, such as projection mapping or append-time uniqueness;
|
|
205
|
+
do not replay a projection merely to feed a pure command decision.
|
|
200
206
|
|
|
201
207
|
Both builder methods return `void`; there is nothing to await.
|
|
202
208
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
209
|
+
Read protection is invisible to application spec authors. Never configure modes,
|
|
210
|
+
call `UseDecisionReads`, simulate competing appends (`AppendConcurrently`), seed
|
|
211
|
+
events solely for infrastructure, or re-prove the platform concurrency/guard
|
|
212
|
+
matrix: give state, assert the outcome. Keep one cheap assembly-level
|
|
213
|
+
`DecisionReadAdmissions.ShouldAdmitDecisionReadsIn(assembly)` spec for the
|
|
214
|
+
application's own model/projection shapes: its only decision-read spec obligation.
|
|
215
|
+
If the harness cannot accept required state, report the upstream gap; never weaken
|
|
216
|
+
production guards for test speed. Read
|
|
217
|
+
[query and decision scenarios](references/query-and-decision-scenarios.md) for
|
|
218
|
+
query pipelines.
|
|
210
219
|
|
|
211
220
|
Validator and `Provide()` dependencies are registered in `_scenario.Services`;
|
|
212
221
|
the concrete validator itself is discovered automatically.
|
|
213
222
|
|
|
214
|
-
## Step 6 — Assert twice on
|
|
223
|
+
## Step 6 — Assert twice on pipeline validation failures
|
|
215
224
|
|
|
216
225
|
```csharp
|
|
217
226
|
[Fact] void should_not_succeed() => _result.ShouldNotBeSuccessful();
|
|
@@ -219,7 +228,9 @@ the concrete validator itself is discovered automatically.
|
|
|
219
228
|
```
|
|
220
229
|
|
|
221
230
|
`ShouldNotBeSuccessful()` alone cannot distinguish a validation rejection from an
|
|
222
|
-
unhandled exception, so both facts are required
|
|
231
|
+
unhandled exception, so both facts are required for pipeline validation specs.
|
|
232
|
+
For direct validator calls, assert the validation result and the specific rule.
|
|
233
|
+
**Do not assert on a message
|
|
223
234
|
string by default** — it is presentation text; assert one only when that exact
|
|
224
235
|
text is the specified behavior, and name that requirement in the fact. A `.play`
|
|
225
236
|
specification may pin the message (`then error "<message>"`); prefer pinning the
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Contents
|
|
4
4
|
|
|
5
5
|
- Query pipeline
|
|
6
|
-
-
|
|
6
|
+
- Decision specifications
|
|
7
7
|
- Sources
|
|
8
8
|
|
|
9
9
|
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
@@ -47,8 +47,10 @@ arbitrary stores.
|
|
|
47
47
|
Requires Arc 22.87.0 and Chronicle 19.54.0 for scenario `IReadModels.Observe<T>(filter)` snapshots from given events or pinned models,
|
|
48
48
|
including new emissions after scenario command appends. Pins need a readable key
|
|
49
49
|
or throw `PinnedReadModelCannotBeObserved`.
|
|
50
|
-
Requires Arc 22.91.1 and Chronicle 19.59.1 for
|
|
51
|
-
observe
|
|
50
|
+
Requires Arc 22.91.1 and Chronicle 19.59.1 for children, joins and reducers to
|
|
51
|
+
observe through scenario-owned storage (the initial Arc package was not published).
|
|
52
|
+
Check both package versions; upgrade or report a harness gap rather than configuring
|
|
53
|
+
decision-read modes in application specs.
|
|
52
54
|
|
|
53
55
|
For a query with `by`, `filter` or `scoped to`, seed instances for two keys, two filter values
|
|
54
56
|
or two tenants with distinct callers, as applicable, and assert that each call returns only its
|
|
@@ -65,50 +67,50 @@ execution runs the query through Arc's hosted query pipeline and establishes con
|
|
|
65
67
|
before `Provide`/`Handle` on the command path. A direct static query call does not
|
|
66
68
|
cover those pipeline hooks.
|
|
67
69
|
|
|
68
|
-
##
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
`
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
70
|
+
## Decision specifications
|
|
71
|
+
|
|
72
|
+
Start with a direct decision call supplied with read-model state. Follow the
|
|
73
|
+
[scenario rule](../../../rules/specs.scenarios.csharp.md): application authors do
|
|
74
|
+
not select read-protection modes, call `UseDecisionReads`, seed events only to
|
|
75
|
+
satisfy infrastructure, or prove the platform concurrency, conflict-refusal or
|
|
76
|
+
isolation matrix. Arc/Chronicle specs own it. The application's only decision-read
|
|
77
|
+
spec obligation is one cheap assembly-level check of its own model/projection
|
|
78
|
+
shapes, not one scenario per decision:
|
|
79
|
+
|
|
80
|
+
```csharp
|
|
81
|
+
DecisionReadAdmissions.ShouldAdmitDecisionReadsIn(assembly);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This API is in `Cratis.Arc.Chronicle.Testing.Commands`; it discovers declared reads
|
|
85
|
+
and checks admission without executing commands or running Chronicle. Keep the
|
|
86
|
+
spec's discovered projections representative of production; spec-only competing
|
|
87
|
+
definitions can make a model ambiguous. Explicit `IDecisionReads.Get` calls inside
|
|
88
|
+
method bodies are not statically discovered. If the harness cannot accept
|
|
89
|
+
required state for a business decision, report the upstream gap; never weaken
|
|
90
|
+
production guards or invent guard doubles.
|
|
91
|
+
|
|
92
|
+
Do not add a scenario to prove a race. A rule such as "two callers reserving the last
|
|
93
|
+
seat yield one reservation" is specified from state: supply a read model with no seats
|
|
94
|
+
left and assert the rejection, then supply one seat and assert the reservation.
|
|
95
|
+
Simulating competing appends is a framework concern a developer should not think
|
|
96
|
+
about in tests; Arc and Chronicle prove it. `AppendConcurrently` stays a public but
|
|
97
|
+
advanced framework-verification API (hidden from IntelliSense), outside application
|
|
98
|
+
testing guidance; use it only for a specific, explicit need. Production expresses
|
|
99
|
+
the guarantee with the read model as a `Handle` parameter under
|
|
100
|
+
`[ReadProtection(On)]`. Direction, not a release claim: Arc will obsolete
|
|
101
|
+
`UseDecisionReads`; do not adopt it.
|
|
102
|
+
`ShouldHaveCompletedStream(ClosedStreamScope)` may assert that a decision completed a
|
|
103
|
+
stream at the exact source and route (`Cratis.Arc.Chronicle.Testing.Commands`).
|
|
104
|
+
Use event history only when it is part of the domain example.
|
|
105
|
+
|
|
106
|
+
The Chronicle-backed `CommandScenario` discovers constraints automatically.
|
|
107
|
+
Assert domain constraint refusals instead of disabling them; use `EventScenario`
|
|
108
|
+
for append-time uniqueness examples. Do not subclass or substitute its engine.
|
|
109
|
+
Provider boundaries remain separate: state fixtures are not a MongoDB substitute.
|
|
108
110
|
|
|
109
111
|
## Sources
|
|
110
112
|
|
|
111
113
|
- [Query scenarios](https://github.com/Cratis/Arc/blob/main/Documentation/backend/csharp/testing/query-scenario.md)
|
|
112
114
|
- [Stream decisions and routed specs](https://github.com/Cratis/Arc/blob/main/Documentation/backend/csharp/chronicle/commands/stream-decisions.md)
|
|
113
115
|
- [Decision-mode extension](https://github.com/Cratis/Arc/blob/main/Source/DotNET/Chronicle.Testing/Commands/CommandScenarioChronicleExtensions.cs)
|
|
114
|
-
- [
|
|
116
|
+
- [Application-shape admission assertion](https://github.com/Cratis/Arc/blob/main/Source/DotNET/Chronicle.Testing/Commands/DecisionReadAdmissions.cs)
|
|
@@ -274,6 +274,11 @@ Components guidance, not to this skill.
|
|
|
274
274
|
|
|
275
275
|
## Verify
|
|
276
276
|
|
|
277
|
+
Start pure `Handle()` and validator decisions with direct calls and supplied
|
|
278
|
+
read-model state. Add scenarios only for pipeline/authorization wiring,
|
|
279
|
+
constraints, never read-protection plumbing or simulated concurrency;
|
|
280
|
+
follow the [scenario selection and cost rule](../../rules/specs.scenarios.csharp.md).
|
|
281
|
+
|
|
277
282
|
- The command is a `record`, carries `[Command]`, and declares a public instance
|
|
278
283
|
`Handle`.
|
|
279
284
|
- The return shape matches what the command is supposed to do, and any
|
|
@@ -15,6 +15,13 @@ Source: Arc. These features require `Cratis.Arc.Chronicle`.
|
|
|
15
15
|
|
|
16
16
|
## Pick the guarded read
|
|
17
17
|
|
|
18
|
+
**Idiomatic default:** a state-dependent rule takes the read model as a `Handle()`
|
|
19
|
+
(or validator-constructor) parameter, with `[ReadProtection(On)]` where it must hold
|
|
20
|
+
under concurrent writers. Do not take `IDecisionReads` in `Handle()`: it is not
|
|
21
|
+
recommended with Arc and is not an investment target. A computed key stays in
|
|
22
|
+
`Provide()` until a read-model parameter covers it. Direction, not a release claim:
|
|
23
|
+
Arc will pin protected parameters so the idiomatic shape needs no further ceremony.
|
|
24
|
+
|
|
18
25
|
Under read protection `On` (`<CratisReadProtection>On</CratisReadProtection>`, or
|
|
19
26
|
`[ReadProtection(ReadProtectionMode.On)]` on a command), a read model that is a
|
|
20
27
|
parameter of `Handle()` or of a `CommandValidator<T>` constructor is read fresh from
|
|
@@ -175,20 +182,21 @@ source id/type and stream route; reactor tuples/collections cannot mix it with e
|
|
|
175
182
|
|
|
176
183
|
## Decision specifications
|
|
177
184
|
|
|
178
|
-
`
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
185
|
+
Call `Handle()` and validators directly with read-model state for pure business
|
|
186
|
+
decisions. Follow the [scenario rule](../../../rules/specs.scenarios.csharp.md):
|
|
187
|
+
read-protection plumbing is invisible to application spec authors. They never
|
|
188
|
+
call `UseDecisionReads`, configure modes, simulate competing appends
|
|
189
|
+
(`AppendConcurrently`), seed events solely for infrastructure, or re-prove the platform concurrency, conflict-refusal or isolation matrix;
|
|
190
|
+
Arc/Chronicle specs own it. The application's only decision-read spec obligation
|
|
191
|
+
is one cheap assembly-level shape-admission spec from that rule.
|
|
192
|
+
Never weaken production guards for test speed. Report a harness state-setup gap
|
|
193
|
+
upstream instead of working around it in application specs.
|
|
194
|
+
|
|
195
|
+
Add scenarios for pipeline/authorization wiring or constraints. Never simulate
|
|
196
|
+
competing appends: assert the business outcome from supplied state (for example, no
|
|
197
|
+
copies left rejects). Read
|
|
198
|
+
[query and decision scenarios](../../cratis-application-slice-specifications/references/query-and-decision-scenarios.md)
|
|
199
|
+
for the query and decision setup. Keep shared lifecycle on and measure the added cost.
|
|
192
200
|
|
|
193
201
|
## Sources
|
|
194
202
|
|
|
@@ -98,18 +98,16 @@ use an appropriate constraint or explicit concurrency scope for them.
|
|
|
98
98
|
|
|
99
99
|
## Specify the collision
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
Run `dotnet build -c Debug` and the affected specification project with
|
|
115
|
-
`dotnet test`; all collision and empty-source facts must pass.
|
|
101
|
+
The generated-value collision guard and its route, append and rollback matrix
|
|
102
|
+
belong in Arc/Chronicle specs, not every application. Follow the
|
|
103
|
+
[scenario rule](../../../rules/specs.scenarios.csharp.md): application authors
|
|
104
|
+
supply state and assert business decisions without configuring decision reads
|
|
105
|
+
or seeding events solely to satisfy infrastructure.
|
|
106
|
+
|
|
107
|
+
Add a scenario only for an application-owned collision/retry policy or generated
|
|
108
|
+
parameter pipeline wiring that a direct call cannot prove. `PinGeneratedValue(name, id)`
|
|
109
|
+
can make that domain example deterministic; assert its specified outcome, not
|
|
110
|
+
the platform guard matrix. If a harness rejects the state setup (for example
|
|
111
|
+
`GeneratedIdentifierCollisionRequiresDecisionReads`), report the upstream gap
|
|
112
|
+
rather than calling compatibility switches or weakening production guards.
|
|
113
|
+
Build the affected project and run its specifications with the project runner.
|
|
@@ -266,15 +266,19 @@ authorization and camelCase member paths are separate contracts.
|
|
|
266
266
|
|
|
267
267
|
## Step 6 — Specify the rejection
|
|
268
268
|
|
|
269
|
-
Every rule gets a specification
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
(
|
|
274
|
-
`
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
269
|
+
Every rule gets a failure specification: instantiate the validator with its
|
|
270
|
+
read-model state and validate directly, asserting the specific rejection. Add
|
|
271
|
+
`CommandScenario<TCommand>` only for pipeline/authorization wiring, constraints.
|
|
272
|
+
Follow the
|
|
273
|
+
[scenario rule](../../rules/specs.scenarios.csharp.md): application specs never
|
|
274
|
+
configure read-protection modes, call `UseDecisionReads`, simulate competing appends, seed events solely for
|
|
275
|
+
infrastructure, or re-prove platform guards. Keep the single cheap assembly-level
|
|
276
|
+
application-shape admission spec specified by that rule; it is the application's
|
|
277
|
+
only decision-read spec obligation. Report harness gaps upstream; never weaken
|
|
278
|
+
production guards for speed.
|
|
279
|
+
|
|
280
|
+
For a pipeline validation rejection, assert **both** that the command did not
|
|
281
|
+
succeed and that it failed *as a validation*:
|
|
278
282
|
|
|
279
283
|
```csharp
|
|
280
284
|
[Fact] void should_not_succeed() => _result.ShouldNotBeSuccessful();
|
|
@@ -309,6 +313,8 @@ the fact. See
|
|
|
309
313
|
|
|
310
314
|
`dotnet build` in Debug and in Release, zero warnings and zero errors — the
|
|
311
315
|
analyzers above are how `ARC0005`, `ARC0006` and `ARC0013` surface. Then
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
316
|
+
run the specification project using its runner, with a direct validation
|
|
317
|
+
specification per rule and focused pipeline specs where wiring matters. Pipeline
|
|
318
|
+
validation specs assert both `ShouldNotBeSuccessful()` and
|
|
319
|
+
`ShouldHaveValidationErrors()`. A rule with no red-first specification has not
|
|
320
|
+
been shown to reject anything.
|
|
@@ -87,8 +87,12 @@ NativeAOT support for the unrelated dynamic validator opt-in.
|
|
|
87
87
|
|
|
88
88
|
## Specify the boundary
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
In application specs, call the validator directly with supplied read-model state
|
|
91
|
+
and assert its business decision. Keep the one cheap assembly-level application-shape
|
|
92
|
+
admission spec in [specs.scenarios.csharp.md](../../../rules/specs.scenarios.csharp.md).
|
|
93
|
+
|
|
94
|
+
Arc/Chronicle specs own the construction/guard matrix: fresh instances, callback
|
|
95
|
+
failures, refused DI graphs, cleanup, key matching and repeated execution. Do not
|
|
96
|
+
re-prove that matrix per application validator. A focused application pipeline
|
|
97
|
+
spec may prove its own registration/wiring; advisory Validate success alone is
|
|
98
|
+
not proof that Execute commits.
|
|
@@ -188,12 +188,16 @@ deciding writer is guarded or the rule is a constraint.
|
|
|
188
188
|
|
|
189
189
|
## Verify
|
|
190
190
|
|
|
191
|
-
-
|
|
192
|
-
|
|
193
|
-
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
-
|
|
191
|
+
- Review production enrollment in the intended store, namespace and stream;
|
|
192
|
+
surface refusal and never replace it with an unprotected read. One owner completes.
|
|
193
|
+
- Application specs call `Handle()` or the validator directly with supplied state.
|
|
194
|
+
Their only decision-read spec obligation is one cheap assembly-level
|
|
195
|
+
`DecisionReadAdmissions.ShouldAdmitDecisionReadsIn(assembly)` check of the
|
|
196
|
+
application's own model/projection shapes; see the
|
|
197
|
+
[scenario rule](../../rules/specs.scenarios.csharp.md).
|
|
198
|
+
- Arc/Chronicle specs alone prove platform conflict refusal, relevant/irrelevant
|
|
199
|
+
changes, absence guards, empty checked appends, isolation and commit lifecycle.
|
|
200
|
+
Their real-client/kernel integration proves the boundary; a default
|
|
199
201
|
`EventScenario` scope strategy returning `None` is not concurrency evidence.
|
|
202
|
+
- Application specs never simulate competing appends or repeat that matrix.
|
|
203
|
+
Never weaken production guards for spec speed.
|
package/package/corpus/skills/cratis-chronicle-decision-reads/references/admission-and-ownership.md
CHANGED
|
@@ -28,8 +28,12 @@ an adapter for arbitrary read-model storage:
|
|
|
28
28
|
Whole-source key admission supports string/Guid schemas, with canonical lowercase
|
|
29
29
|
D-form Guid source ids assumed. A renamed or custom-converted key is not made
|
|
30
30
|
safe by a cast in the caller. Runtime definition comparison is a diagnostic, not
|
|
31
|
-
proof of equality with an executing projection.
|
|
32
|
-
|
|
31
|
+
proof of equality with an executing projection. Arc/Chronicle own kernel-pair
|
|
32
|
+
integration and the platform guard matrix. Application specs have just one cheap
|
|
33
|
+
assembly-level shape-admission check,
|
|
34
|
+
`DecisionReadAdmissions.ShouldAdmitDecisionReadsIn(assembly)`; see the
|
|
35
|
+
[scenario rule](../../../rules/specs.scenarios.csharp.md). It checks the
|
|
36
|
+
application's own discovered models/projections without running Chronicle.
|
|
33
37
|
|
|
34
38
|
Do not silently replace a refused decision read with a materialized sink read.
|
|
35
39
|
Redesign the decision model as a focused admitted projection only when the
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- Arc traps
|
|
13
13
|
- Chronicle-only traps
|
|
14
14
|
- Roll out with Measure
|
|
15
|
-
-
|
|
15
|
+
- Specify the business decision
|
|
16
16
|
- Costs
|
|
17
17
|
- Checklist for generated code
|
|
18
18
|
|
|
@@ -82,7 +82,7 @@ published scenario catalogue.
|
|
|
82
82
|
of the same source in one command are refused.
|
|
83
83
|
11. **Moving an existing app to `On`** (S12): `Measure` first.
|
|
84
84
|
12. **A command failed under protection** (S13): the [refusals](#refusals).
|
|
85
|
-
13. **Prove it** (S14): [
|
|
85
|
+
13. **Prove it** (S14): [specify the business decision](#specify-the-business-decision).
|
|
86
86
|
|
|
87
87
|
## The matrix
|
|
88
88
|
|
|
@@ -256,7 +256,7 @@ return `Commit()` last), `ReadOfSourceWithStagedEvents`, `ExplicitScopeDoesNotCo
|
|
|
256
256
|
| Injected `IEventLog.Append` in `Handle` | immediate write that survives a conflict rollback | return events |
|
|
257
257
|
| Aggregate `Commit()` whose result is ignored, or `Failed()` without `Commit()` | staged events still commit | return `await aggregate.Commit()` |
|
|
258
258
|
| `[Unprotected]` command taking `DecisionRead<T>` | resolves to an unguarded read | remove `[Unprotected]` |
|
|
259
|
-
|
|
|
259
|
+
| Per-decision specs rebuild the platform guard matrix | repeated infrastructure proof, not business coverage | [specify the business decision](#specify-the-business-decision) |
|
|
260
260
|
|
|
261
261
|
## Chronicle-only traps
|
|
262
262
|
|
|
@@ -304,31 +304,23 @@ then set `On` per project. Behaviour for callers does not change under `Measure`
|
|
|
304
304
|
or annotate `[Unprotected("why")]`. Under `On`, `ARCCHR0011` no longer applies;
|
|
305
305
|
fix `ARCCHR0024`, `0028` and `0033`.
|
|
306
306
|
|
|
307
|
-
##
|
|
308
|
-
|
|
309
|
-
`
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
var result = await scenario.Execute(new BorrowBook(isbn, me));
|
|
325
|
-
result.ShouldHaveValidationErrorBecauseOf(ValidationResultReason.ConcurrencyViolation);
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
There is no `scenario.AppendConditions`. A pure decision test builds the command
|
|
329
|
-
and the validator with a hand-made read model. Chronicle-direct code asserts
|
|
330
|
-
`unitOfWork.GetAppendConditions()`. A default `EventScenario` scope strategy
|
|
331
|
-
returning `None` is not concurrency evidence.
|
|
307
|
+
## Specify the business decision
|
|
308
|
+
|
|
309
|
+
Start with a plain `Handle()` or validator call supplied with read-model state.
|
|
310
|
+
Follow the [scenario rule](../../../rules/specs.scenarios.csharp.md): read-protection
|
|
311
|
+
plumbing is invisible to application spec authors. They never configure modes,
|
|
312
|
+
call `UseDecisionReads`, seed events solely for infrastructure or re-prove platform
|
|
313
|
+
concurrency, conflict-refusal or isolation guards. Arc/Chronicle specs own that
|
|
314
|
+
matrix. The application's only decision-read spec obligation is one cheap
|
|
315
|
+
assembly-level `DecisionReadAdmissions.ShouldAdmitDecisionReadsIn(assembly)`
|
|
316
|
+
check of its own models/projections, not per-decision scenarios. Production
|
|
317
|
+
protection stays unchanged regardless of spec cost.
|
|
318
|
+
|
|
319
|
+
Do not simulate competing appends in an application spec, even for a rule such as
|
|
320
|
+
two borrowers competing for the last copy. Give the state (no copies left) and assert
|
|
321
|
+
the domain outcome (rejected); the guarantee that only one wins is proven by Arc and
|
|
322
|
+
Chronicle. If the harness cannot accept required state, report the upstream gap
|
|
323
|
+
rather than adding infrastructure setup to application specs.
|
|
332
324
|
|
|
333
325
|
## Costs
|
|
334
326
|
|
|
@@ -349,7 +341,8 @@ append. Decision reads exist in the .NET and Elixir clients.
|
|
|
349
341
|
an explicit scope, enrolled **before** the events are staged.
|
|
350
342
|
- The model is admitted (source-keyed projection or admitted reducer, `Id`).
|
|
351
343
|
- Cross-key rules append one event per source atomically, with distinct event types.
|
|
352
|
-
-
|
|
353
|
-
|
|
344
|
+
- Business decisions have direct `Handle()`/validator specs with supplied state;
|
|
345
|
+
one cheap assembly-level spec checks application-shape admission. Arc/Chronicle,
|
|
346
|
+
not application specs, interleave appends to prove the platform guard matrix.
|
|
354
347
|
- What stays unguarded (an unguarded writer's own decision, external state,
|
|
355
348
|
revisions, `[Unprotected]` reads, `Off` mode) is stated.
|
|
@@ -52,10 +52,11 @@ version. Never translate an assertion name from memory.
|
|
|
52
52
|
(`When`, or `EventLog.Append` without a `concurrencyScope`) can never report
|
|
53
53
|
`HasConcurrencyViolations`. Only an append that passes an explicit scope on
|
|
54
54
|
`scenario.EventLog.Append(...)` reaches the in-process validator at all — and
|
|
55
|
-
that is a spec of the scope you hand-built, not of the application's strategy.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
that is a spec of the scope you hand-built, not of the application's strategy. The platform concurrency/guard matrix belongs only in Arc/Chronicle specs,
|
|
56
|
+
with real-kernel integration where required. Application decisions use direct
|
|
57
|
+
`Handle()`/validator calls with supplied state plus the one cheap assembly-level
|
|
58
|
+
shape-admission spec in [specs.scenarios.csharp.md](../../rules/specs.scenarios.csharp.md).
|
|
59
|
+
A default scenario must not claim concurrency coverage — it would pass vacuously.
|
|
59
60
|
|
|
60
61
|
## Step 1 — Create the scenario
|
|
61
62
|
|
|
@@ -15,6 +15,11 @@ default, with the event and current state supplied explicitly (for a self model,
|
|
|
15
15
|
`Model.Reduce(@event, current)`). Add
|
|
16
16
|
`ReadModelScenario<TReadModel>` only where declarative projection behavior,
|
|
17
17
|
event dispatch, filtering, metadata or read-model wiring contribute proof.
|
|
18
|
+
Follow the [scenario selection and cost rule](../../rules/specs.scenarios.csharp.md):
|
|
19
|
+
use one scenario class per read model, many read-only assertions on one shared
|
|
20
|
+
setup, and keep shared lifecycle enabled. Do not create a class per property,
|
|
21
|
+
mapping attribute or lifecycle event. Measure scenario cost with TRX durations
|
|
22
|
+
using [running-specs.md](../cratis-specifications-csharp/references/running-specs.md).
|
|
18
23
|
|
|
19
24
|
For a read-model scenario specification the **`Given` *is* the act**. There
|
|
20
25
|
is no separate `When`: seeding the events is what makes the projection run.
|
|
@@ -69,7 +74,8 @@ version.
|
|
|
69
74
|
|
|
70
75
|
## Step 1 — Create the scenario
|
|
71
76
|
|
|
72
|
-
|
|
77
|
+
Create one scenario for the read model's specification class. Share setup among
|
|
78
|
+
its read-only facts, never across specification classes.
|
|
73
79
|
|
|
74
80
|
```csharp
|
|
75
81
|
// Default, empty initial state
|
|
@@ -214,7 +220,11 @@ public class and_two_items_are_priced : Specification
|
|
|
214
220
|
|
|
215
221
|
Wrap files in `#if DEBUG … #endif` only when they compile into the application
|
|
216
222
|
assembly, so specification code ships only in Debug. Dedicated spec projects
|
|
217
|
-
need no wrapper. Keep one outcome per `should_` fact
|
|
223
|
+
need no wrapper. Keep one outcome per `should_` fact, not one scenario class per
|
|
224
|
+
assertion. Apply the event sequence once; capture intermediate states in `Because`
|
|
225
|
+
for create/update/delete assertions so facts remain read-only. Capture values or
|
|
226
|
+
immutable copies if later events mutate the same instance. Multiple event-source
|
|
227
|
+
keys may share this setup for isolation and child-key assertions.
|
|
218
228
|
|
|
219
229
|
## Step 6 — Cover the read model, not just one event
|
|
220
230
|
|
|
@@ -224,7 +234,8 @@ One summation example proves one event. Take the inventory from the contract
|
|
|
224
234
|
(the `.play` read model and its specifications, or the agreed outline); without
|
|
225
235
|
one, derive it from the projection and say so. Then check each line:
|
|
226
236
|
|
|
227
|
-
- **Every property is asserted** in
|
|
237
|
+
- **Every property is asserted** in a read-only fact in the model's shared
|
|
238
|
+
scenario class; do not multiply scenario startup for each field.
|
|
228
239
|
- **Every subscribed event has a specification.** Reconcile three lists in both
|
|
229
240
|
directions: the contract's event types, the projection's actual subscriptions,
|
|
230
241
|
and the events your specifications seed. Report any event in one list missing
|