@cratis/pi 7.27.3 → 7.28.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.
Files changed (59) hide show
  1. package/package/corpus/rules/reactors.md +30 -0
  2. package/package/corpus/rules/specs.scenarios.csharp.md +41 -0
  3. package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +13 -0
  4. package/package/corpus/skills/cratis-arc-command/SKILL.md +34 -11
  5. package/package/corpus/skills/cratis-arc-command/references/generated-values.md +166 -0
  6. package/package/corpus/skills/cratis-chronicle-reactor/SKILL.md +16 -1
  7. package/package/corpus/skills/cratis-chronicle-reactor/references/specifications.md +85 -0
  8. package/package/corpus/skills/cratis-fundamentals-concept/SKILL.md +13 -5
  9. package/package/corpus/skills/cratis-screenplay-automations-and-translations/SKILL.md +3 -2
  10. package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/audit-format.md +2 -2
  11. package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/automation-patterns.md +4 -2
  12. package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/provenance.md +5 -3
  13. package/package/corpus/skills/cratis-screenplay-captures-and-reactions/SKILL.md +3 -2
  14. package/package/corpus/skills/cratis-screenplay-captures-and-reactions/references/invocation-identity.md +6 -5
  15. package/package/corpus/skills/cratis-screenplay-captures-and-reactions/references/specifying-triggers-and-clock.md +1 -1
  16. package/package/corpus/skills/cratis-screenplay-command-surface/SKILL.md +4 -4
  17. package/package/corpus/skills/cratis-screenplay-command-surface/references/context.md +6 -5
  18. package/package/corpus/skills/cratis-screenplay-command-surface/references/generated-values-and-responses.md +5 -5
  19. package/package/corpus/skills/cratis-screenplay-event-modeling/SKILL.md +4 -3
  20. package/package/corpus/skills/cratis-screenplay-event-modeling/references/parsed-not-runnable.md +4 -4
  21. package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/generate-notes.md +6 -4
  22. package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +10 -7
  23. package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-tools.md +60 -10
  24. package/package/corpus/skills/cratis-screenplay-model-authoring/references/screen-authoring-workflow.md +6 -5
  25. package/package/corpus/skills/cratis-screenplay-model-authoring/verification.json +11 -1
  26. package/package/corpus/skills/cratis-screenplay-model-review/SKILL.md +29 -5
  27. package/package/corpus/skills/cratis-screenplay-model-review/references/anti-patterns.md +1 -1
  28. package/package/corpus/skills/cratis-screenplay-read-surface/references/guarded-actions.md +2 -2
  29. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/SKILL.md +12 -11
  30. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/gap-fill-handoff.md +3 -2
  31. package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/spec-forms-by-mode.md +4 -4
  32. package/package/corpus/skills/cratis-screenplay-slice-design/references/worked-example.md +1 -1
  33. package/package/corpus/skills/cratis-screenplay-specifications/SKILL.md +2 -2
  34. package/package/corpus/skills/cratis-screenplay-specifications/references/examples-and-routes.md +1 -1
  35. package/package/corpus/skills/cratis-screenplay-streams-and-consistency/SKILL.md +3 -3
  36. package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/consistency-and-concurrency.md +2 -2
  37. package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/stream-patterns.md +1 -1
  38. package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +6 -6
  39. package/package/corpus/skills/cratis-screenplay-toolchain/references/cheat-sheet.md +1 -1
  40. package/package/corpus/skills/cratis-screenplay-toolchain/references/diagnostics.md +1 -1
  41. package/package/corpus/skills/cratis-screenplay-toolchain/references/doc-contradictions.md +1 -1
  42. package/package/corpus/skills/cratis-screenplay-toolchain/references/executable-subset.md +4 -3
  43. package/package/corpus/skills/cratis-screenplay-toolchain/references/provenance.md +7 -1
  44. package/package/corpus/skills/cratis-screenplay-toolchain/references/renderable-subset.md +7 -6
  45. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +364 -28
  46. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
  47. package/package/corpus/skills/cratis-screenplay-toolchain/references/sources-and-streams.md +1 -1
  48. package/package/corpus/skills/cratis-screenplay-toolchain/references/traps.md +4 -4
  49. package/package/corpus/skills/cratis-screenplay-toolchain/references/verdicts.md +4 -4
  50. package/package/corpus/skills/cratis-screenplay-toolchain/references/versions.md +23 -13
  51. package/package/corpus/skills/cratis-screenplay-toolchain/verification.json +1 -1
  52. package/package/corpus/skills/cratis-screenplay-ui-composition/references/screen-composition-contract.md +6 -6
  53. package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +34 -0
  54. package/package/corpus/skills/cratis-specifications-csharp/references/application-scenarios.md +121 -4
  55. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +18 -8
  56. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +5 -3
  57. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/case-tables-and-whole-numbers.md +127 -0
  58. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/verification.json +5 -1
  59. package/package.json +1 -1
@@ -278,4 +278,34 @@ async Task Because() => await _scenario.Given.ForEventSource(_id).Events(new Inv
278
278
  [Fact] async Task should_notify_about_the_issued_invoice() => await _notifications.Received(1).Notify(Arg.Is<string>(_ => _.Contains("INV-1001")));
279
279
  ```
280
280
 
281
+ ### Read-model dependencies in scenarios — Chronicle 19.46.0+
282
+
283
+ `ReactorScenario<TReactor>` does **not** build read models from `Given` events,
284
+ including `[Passive]` models. Seed the handler's read-model parameter explicitly
285
+ before firing the trigger:
286
+
287
+ ```csharp
288
+ void Establish() => _scenario.Given.ForEventSourceId(_id).ReadModel(_order);
289
+ async Task Because() => await _scenario.Given.ForEventSource(_id).Events(_orderPlaced);
290
+ ```
291
+
292
+ In a separate absence specification, replace the seeding with
293
+ `_scenario.Given.ForEventSourceId(_id).NoReadModel<Order>()` to deliberately pass
294
+ `null` to an `Order?` parameter. Without either setup, resolution to `null` throws
295
+ `ReadModelNotSeededForReactorScenario` (`Cratis.Chronicle.Testing.Reactors`), even
296
+ for a nullable parameter. Its message names the type and missing key, suggests
297
+ both setup methods, and lists other seeded keys for that type when any exist.
298
+ Use the key from `ICanResolveReadModelKey.Resolve` for seeding or absence when
299
+ implemented, not the triggering source id. `NoReadModel<T>()` does not remove a
300
+ seeded instance. This guard applies to `EventStoreForTesting`, not an explicitly
301
+ supplied store of another type; production read-model resolution is unchanged.
302
+
303
+ Also in Chronicle 19.46.0+, `Events(...)` surfaces a synchronously throwing
304
+ handler's own exception, preserving its stack, rather than the reflection
305
+ `TargetInvocationException`. Capture it with `Catch.Exception` and assert the
306
+ handler's exception type. `EventContext.From(...)` now defaults an omitted
307
+ `occurred` to `DateTimeOffset.UtcNow`, not local `Now`; scenario contexts,
308
+ `EventContext.Empty` and `EmptyWithEventSourceId(...)` use that helper. An
309
+ explicit occurrence time is preserved, not normalized to UTC.
310
+
281
311
  For reactors that return side-effect events, assert the resulting appends through the scenario's event store; for non-event side effects, assert on the mocked services (as above). See [specs.scenarios.csharp.md](./specs.scenarios.csharp.md) for the full `*Scenario` family.
@@ -160,6 +160,47 @@ async Task Because() => await _scenario.Given.ForEventSource(_id).Events(new Aut
160
160
  [Fact] async Task should_notify() => await _service.Received(1).Notify("Jane Austen");
161
161
  ```
162
162
 
163
+ #### Read-model setup — Chronicle 19.46.0+
164
+
165
+ In a class-based `ReactorScenario<TReactor>`, `Given` events invoke the reactor;
166
+ they **do not materialize read models**, even `[Passive]` ones. For each
167
+ read-model handler parameter, choose one setup before `Because()`:
168
+
169
+ ```csharp
170
+ // Present-state specification: supply the instance the handler must receive.
171
+ void Establish() => _scenario.Given.ForEventSourceId(_id).ReadModel(_author);
172
+
173
+ // Separate absence specification: deliberately let an Author? parameter receive null.
174
+ void Establish() => _scenario.Given.ForEventSourceId(_id).NoReadModel<Author>();
175
+ ```
176
+
177
+ These are alternatives in **separate specification classes**, not two
178
+ `Establish()` methods in one class. Both builders return
179
+ `ReactorSourceGivenBuilder<TReactor>` for chaining; neither setup is async.
180
+ Use the triggering event-source id by default, or the key returned by the
181
+ reactor's `ICanResolveReadModelKey.Resolve` when implemented. An absence
182
+ declaration is for that exact type and key and does not delete a seeded model.
183
+
184
+ If a recognized read-model parameter resolves to `null` without declared absence,
185
+ `Events(...)` throws `ReadModelNotSeededForReactorScenario` from
186
+ `Cratis.Chronicle.Testing.Reactors`, even if the parameter is nullable. The
187
+ message names the full type and missing key, explains `ReadModel(...)` and
188
+ `NoReadModel<T>()`, and lists seeded keys for that type when any exist. Fix the
189
+ fixture or its key; do not add `NoReadModel<T>()` just to silence missing setup.
190
+ The guard applies to `EventStoreForTesting`; it does not alter production
191
+ resolution or an explicitly supplied event store of another type.
192
+
193
+ **Handler failures:** in Chronicle 19.46.0+, a synchronous handler throw surfaces
194
+ its own exception, not `TargetInvocationException`, with its handler stack
195
+ preserved. Use `await Catch.Exception(() => _scenario.Given.ForEventSource(_id).Events(...))`
196
+ and assert the handler's exception type.
197
+
198
+ **Occurrence time:** in Chronicle 19.46.0+, `EventContext.From(...)` uses
199
+ `DateTimeOffset.UtcNow` when `occurred` is omitted (previously local `Now`).
200
+ `ReactorScenario` builds its contexts through this helper, as do
201
+ `EventContext.Empty` and `EmptyWithEventSourceId(...)`. Explicit occurrence times
202
+ keep their supplied offset; the positional constructor has no such default.
203
+
163
204
  ## Out-of-process Chronicle integration specs (advanced)
164
205
 
165
206
  Reserve these for the host/transport boundary the scenario helpers can't reach. They test a complete slice — HTTP request → command → append → constraint → projection — against a real Chronicle store, and live under `when_<behavior>/` directly inside the slice folder (no `for_` folder; the "unit" is the whole slice).
@@ -30,6 +30,10 @@ This skill decides **when to add a scenario** and **what to cover**. The C# mech
30
30
 
31
31
  > Re-verified at the versions above by **symbol and signature**: every type, attribute and member this skill names exists at that tag, and the public surface it describes is unchanged since the previous verification (Chronicle 16.45.x / Arc 22.10.4 — the Chronicle 16→18 client diff is converters, options and doc comments; no type was removed or renamed). Behavior claims were verified at the earlier tag unless a section says otherwise.
32
32
 
33
+ Reactor read-model setup and the `EventContext.From` UTC default below are
34
+ verified separately at **Chronicle 19.46.0**, the minimum version for that
35
+ guidance; see `cratis-chronicle-reactor/references/specifications.md` for sources.
36
+
33
37
  `Cratis.Testing` is the meta-package that brings `Cratis.Arc.Testing` and
34
38
  `Cratis.Arc.Chronicle.Testing` together. Reverify against the owning product
35
39
  repository before claiming support for another version.
@@ -112,6 +116,15 @@ keep a scenario for that wiring and for appended-event behavior.
112
116
  | Automation or Translation — a reactor | `ReactorScenario<TReactor>` | `cratis-specifications-csharp` |
113
117
  | A host, transport, or real-infrastructure boundary | Out-of-process integration | `cratis-specifications-csharp` |
114
118
 
119
+ For a class-based reactor scenario on **Chronicle 19.46.0+**, explicitly seed
120
+ handler read-model parameters with `Given.ForEventSourceId(id).ReadModel(instance)`
121
+ or declare intentional absence with `.NoReadModel<T>()`. An unseeded model throws
122
+ `ReadModelNotSeededForReactorScenario` even if nullable; `Given` events do not
123
+ build read models, including passive ones. Use the reactor's resolved key, not
124
+ necessarily the trigger's source id. This is **not** the command-scenario event
125
+ seeding described in Step 5. Read `cratis-chronicle-reactor` for the setup examples,
126
+ exception diagnostics, synchronous handler throws and UTC context defaults.
127
+
115
128
  ## Step 3 — Cover every outcome of a State Change slice
116
129
 
117
130
  One specification class for **each** of:
@@ -75,8 +75,9 @@ Rules the framework actually enforces:
75
75
  operation collections, and `ARC0018` checks generated-invoker accessibility.
76
76
  - Name the command as the action — `OpenDebitAccount`, not
77
77
  `OpenDebitAccountCommand`.
78
- - `Handle` parameters are resolved from DI, so ask for services there rather
79
- than through a constructor.
78
+ - Unmarked `Handle` parameters resolve from `Provide()` or DI; ask for services
79
+ there rather than through a constructor. Server-owned new values use
80
+ `[GeneratedValue]` instead (below).
80
81
  - Never inject `IEventLog` into `Handle` to append the command's own events —
81
82
  `ARCCHR0007` warns, and it bypasses Arc's append pipeline. Express the append
82
83
  through the return type.
@@ -137,8 +138,8 @@ Two consequences worth knowing before writing the first command:
137
138
  the events in that same tuple are appended to. Returning more than one
138
139
  unhandleable element throws `MultipleUnhandledTupleValues`. When the command
139
140
  also returns an operation keyed by that id, have the caller supply the id
140
- instead of calling `New()` in `Handle()`, so a retried request reuses the same
141
- ownership key.
141
+ instead of generating a new value, so a retried request reuses the same
142
+ ownership key. A generated value alone does not provide idempotency.
142
143
  - **Events never carry their own event source.** Cross-stream writes use
143
144
  `EventForEventSourceId`; they are not expressed by a property on the event.
144
145
 
@@ -146,11 +147,8 @@ Two consequences worth knowing before writing the first command:
146
147
  [Command]
147
148
  public record Register<Thing>(<ConceptType> <Name>)
148
149
  {
149
- public (<ThingId>, <ThingRegistered>) Handle()
150
- {
151
- var id = <ThingId>.New();
152
- return (id, new(<Name>));
153
- }
150
+ public (<ThingId>, <ThingRegistered>) Handle([GeneratedValue("thing")] <ThingId> id) =>
151
+ (id, new(<Name>));
154
152
  }
155
153
  ```
156
154
 
@@ -166,6 +164,28 @@ public record Transfer(<AccountId> From, <AccountId> To, <Money> Amount)
166
164
  }
167
165
  ```
168
166
 
167
+ ## Generate a server-owned new identifier
168
+
169
+ **Arc 22.66.0+**, verified at **v22.68.0**: the create-command example above
170
+ requires `<ThingId>` to implement `IGeneratable<ThingId>` with a static `New()`
171
+ strategy (Fundamentals 7.23.0+; see `cratis-fundamentals-concept`). Replace
172
+ `Guid.NewGuid()` or `<ThingId>.New()` inside `Handle()` with a generated parameter,
173
+ not a new request property. Return the id as the ordinary response or in the
174
+ id/event tuple so the caller receives it.
175
+
176
+ `[GeneratedValue]` is in `Cratis.Arc.Commands.ModelBound` and applies only to
177
+ parameters of a non-static command `Handle()`. Arc generates after `Provide()`
178
+ control results permit execution, validates the concept, then invokes `Handle`.
179
+ `Provide()` and DI cannot override it. Use a non-nullable self-generatable type,
180
+ not raw `Guid`; names default to the parameter name or an explicit attribute name
181
+ and are case-sensitive, non-empty and unique within the command.
182
+
183
+ Keep client-chosen ids/references as inputs and services/read models as ordinary
184
+ dependencies. A UUID is not a secret, token, authorization proof, idempotency key,
185
+ or a gapless/sequential invoice number. For validation failures, captures,
186
+ ARC0022–ARC0025, migration and the pinned-spec workflow, read
187
+ [generated values](references/generated-values.md).
188
+
169
189
  ## Where the event source id comes from
170
190
 
171
191
  Chronicle resolves the command's event source id in this order:
@@ -330,8 +350,11 @@ Components guidance, not to this skill.
330
350
  - The event source id resolves from exactly one place; `ARCCHR0002` is silent.
331
351
  - `[Key]`, where used, is `Cratis.Chronicle.Keys.KeyAttribute`.
332
352
  - No `IEventLog` is injected into `Handle`.
333
- - Every value `Provide` returns is consumed by `Handle`.
334
- - Rejections are validation results, never thrown exceptions.
353
+ - Every value `Provide` returns is consumed by an unmarked `Handle` parameter.
354
+ - Generated parameters follow [generated values](references/generated-values.md)
355
+ and are pinned before scenario execution (`cratis-specifications-csharp`).
356
+ - User-correctable rejections are validation results, never thrown exceptions;
357
+ `GeneratedValueRejected` instead signals a defective server generation strategy.
335
358
  - Every secret or personal value is marked before it can reach the event log.
336
359
  - `dotnet build` is clean in Debug and Release, with no suppressed `ARC*` or
337
360
  `ARCCHR*` diagnostic left unjustified.
@@ -0,0 +1,166 @@
1
+ # Server-generated Handle values
2
+
3
+ ## Contents
4
+
5
+ - Declare the value and command
6
+ - Resolution, validation and captures
7
+ - Analyzer diagnostics
8
+ - Migrate generation inside Handle
9
+ - Pin generated values in specifications
10
+ - Verified sources and release boundary
11
+
12
+ <!-- Copyright (c) Cratis. All rights reserved. -->
13
+ <!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
14
+
15
+ ## Declare the value and command
16
+
17
+ Generated parameters arrived in Arc **22.66.0**, scenario pinning in **22.67.0**,
18
+ and analyzers in **22.68.0**. The examples and behavior here are verified at
19
+ **v22.68.0**, which pins Fundamentals **7.23.0**.
20
+
21
+ Use a concept for a server-owned new identifier. This standalone Arc example
22
+ allocates an id; it does not append an event or persist a cart:
23
+
24
+ ```csharp
25
+ // Copyright (c) Cratis. All rights reserved.
26
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
27
+
28
+ using Cratis.Arc.Commands.ModelBound;
29
+ using Cratis.Concepts;
30
+
31
+ namespace Shopping.Carts;
32
+
33
+ /// <summary>
34
+ /// Represents a cart identifier.
35
+ /// </summary>
36
+ /// <param name="Value">The underlying identifier.</param>
37
+ public record CartId(Guid Value) : ConceptAs<Guid>(Value), IGeneratable<CartId>
38
+ {
39
+ /// <summary>
40
+ /// Creates a new cart identifier.
41
+ /// </summary>
42
+ /// <returns>A new cart identifier.</returns>
43
+ public static CartId New() => new(GenerateValue.Uuid());
44
+ }
45
+
46
+ /// <summary>
47
+ /// Allocates a server-owned cart identifier.
48
+ /// </summary>
49
+ [Command]
50
+ public record AllocateCartId
51
+ {
52
+ /// <summary>
53
+ /// Returns the allocated identifier to the caller.
54
+ /// </summary>
55
+ /// <param name="cartId">The server-generated identifier.</param>
56
+ /// <returns>The allocated identifier.</returns>
57
+ public CartId Handle([GeneratedValue("cart")] CartId cartId) => cartId;
58
+ }
59
+ ```
60
+
61
+ For a Chronicle stream identity, derive `CartId` from `EventSourceId<Guid>`
62
+ instead; see `cratis-fundamentals-concept`. Return `(id, event)` to both append
63
+ and give the caller the new stream id, as in SKILL.md. An identifier is not an
64
+ event, and generation alone does not persist anything.
65
+
66
+ Keep existing/client-chosen identifiers and references on command properties.
67
+ Services and read models remain ordinary dependencies, not generated values.
68
+ UUID generation does not establish authorization, secrecy, idempotency, storage
69
+ freshness, or gapless/sequential numbering. Use the domain's authoritative
70
+ allocation mechanism for invoice numbers, not a random UUID strategy.
71
+
72
+ ## Resolution, validation and captures
73
+
74
+ `GeneratedValueAttribute` targets parameters only. Use it on a command's public
75
+ instance `Handle()`, not a property, constructor or `Provide()` parameter.
76
+ The type must be non-nullable and implement `IGeneratable<TSelf>` for itself;
77
+ a raw `Guid`, nullable concept or by-reference parameter is invalid. Use domain
78
+ concepts; the runtime's type check is the self-generatable interface contract.
79
+
80
+ `[GeneratedValue]` uses the parameter name. `[GeneratedValue("cart")]` keeps the
81
+ pin/capture name stable across a parameter rename. Names are case-sensitive,
82
+ non-whitespace and unique within the command.
83
+
84
+ Arc first runs `Provide()` and checks its control results. Only if execution may
85
+ continue does it call the concept's `New()` strategy and validate the value before
86
+ invoking `Handle()`. Neither a provided value nor a DI registration overrides a
87
+ generated parameter. Unmarked parameters retain their normal resolution.
88
+
89
+ Discoverable concept validators run with the command's scoped provider and
90
+ cancellation token. Errors produce `GeneratedValueRejected`, an exception
91
+ outcome in `CommandResult`: a server-generation defect, not a request-validation
92
+ 400. Warnings/information allow execution. Fix the strategy or invariant; do not
93
+ turn the defect into a user rejection or disable validation to make it pass.
94
+
95
+ Validated values enter `CommandContext.GeneratedValues` in parameter-resolution
96
+ order. Each capture has zero-based `Order`, `Name`, declared `Type` and `Value`;
97
+ `Get<CartId>("cart")` retrieves the latest matching capture. Rejected values are
98
+ not captured. A Handle parameter is not a request property and is not audited
99
+ as command input. Do not log or trace captures or validator failure text, which
100
+ can contain values. Returning a value or putting it in an event still follows the
101
+ ordinary response, event and compliance contracts.
102
+
103
+ ## Analyzer diagnostics
104
+
105
+ Arc v22.68.0 reports these at compile time; none has a code fix. Runtime
106
+ handler-construction checks remain for declarations compiled without analyzers.
107
+
108
+ | Diagnostic | Severity | Fix |
109
+ | --- | --- | --- |
110
+ | ARC0022 | Error | Use a non-nullable type implementing `IGeneratable<TSelf>` for itself; no raw `Guid` or by-reference parameter. |
111
+ | ARC0023 | Error | Move the attribute to a non-static command `Handle()` parameter, not `Provide()`, a read model or controller. |
112
+ | ARC0024 | Error | Give each generated parameter a non-empty unique name; comparison is case-sensitive. |
113
+ | ARC0025 | Warning | Remove a redundant provided value of the generated type, or remove the attribute if `Provide()` should supply it. An unmarked parameter can still consume that provided type. |
114
+
115
+ ## Migrate generation inside Handle
116
+
117
+ For `var id = new CartId(Guid.NewGuid());` or `var id = CartId.New();` in
118
+ `Handle()`, keep the request contract unchanged:
119
+
120
+ 1. Make the concept implement `IGeneratable<CartId>` with static `New()` using
121
+ `GenerateValue.Uuid()` or `GenerateValue.UuidV7()`.
122
+ 2. Replace the local allocation with `[GeneratedValue("cart")] CartId cartId`
123
+ on `Handle()`, then use that parameter in the existing return/operation shape.
124
+ 3. Return the id if the caller needs it; a capture is not an automatic response.
125
+ 4. Pin the concept by generated name in pipeline specifications. For a pure
126
+ direct-call specification, pass the known concept to `Handle()` yourself.
127
+
128
+ Do not migrate caller-owned inputs into server generation. A retried request
129
+ would otherwise allocate a different value; random generation is not an
130
+ idempotency mechanism. UUIDv7 reveals creation time and is not monotonic within
131
+ a millisecond; choose it for index locality only after checking storage ordering.
132
+
133
+ ## Pin generated values in specifications
134
+
135
+ Use `cratis-specifications-csharp` and its `references/application-scenarios.md`
136
+ for the complete pinned example. `Cratis.Arc.Testing.Commands` supplies
137
+ `PinGeneratedValue(name, concept)`, `AllowGeneration()` and the `GeneratedValues`
138
+ capture helpers on `CommandScenario<TCommand>`.
139
+
140
+ Scenarios default to strict: an unpinned generated parameter yields
141
+ `GeneratedValueNotPinned` in the command result, not a throw from `Execute`.
142
+ Configure before execution. Nested pins use both generic arguments at the call
143
+ site: `PinGeneratedValue<TCommand, TOtherCommand>(name, concept)`.
144
+
145
+ ## Verified sources and release boundary
146
+
147
+ Published guides:
148
+
149
+ - [Generated values](/arc/backend/csharp/commands/generated-values/)
150
+ - [Pin generated values in command specs](/arc/backend/csharp/testing/generated-values/)
151
+ - [Migrate generated identifiers](/arc/upgrading/generated-values/)
152
+ - Analyzer pages: [ARC0022](/arc/backend/csharp/code-analysis/arc0022/),
153
+ [ARC0023](/arc/backend/csharp/code-analysis/arc0023/),
154
+ [ARC0024](/arc/backend/csharp/code-analysis/arc0024/),
155
+ [ARC0025](/arc/backend/csharp/code-analysis/arc0025/).
156
+
157
+ Source at **Arc v22.68.0**: `Source/DotNET/Arc.Core/Commands/ModelBound/GeneratedValueAttribute.cs`,
158
+ `Commands/GeneratedValueParameters.cs`, `GeneratedValueFactories.cs`,
159
+ `CommandHandlerArgumentResolver.cs`, `GeneratedValueResolution.cs`;
160
+ `Source/DotNET/Arc.Core.CodeAnalysis/DiagnosticDescriptors.cs`; and
161
+ `Source/DotNET/Testing/Commands/CommandScenarioGeneratedValueExtensions.cs`.
162
+
163
+ This release has no freshness/collision guard, contextual clock/tenant/pattern
164
+ generation, or additional built-in strategies beyond Fundamentals' UUID/UUIDv7
165
+ helpers. Do not teach proposals as available APIs or invent Screenplay `generate`
166
+ syntax for this feature.
@@ -45,6 +45,11 @@ This skill is verified against these exact sources:
45
45
  | `Cratis.Chronicle` | `18.3.0` | `Cratis.Chronicle.Reactors`, `Cratis.Chronicle.Reactors.SideEffects`, `Cratis.Chronicle.EventSequences`, `Cratis.Chronicle.Events` |
46
46
  | `Cratis.Chronicle.CodeAnalysis` | `18.3.0` | `CHR0004`, `CHR0005`, `CHR0008`, `CHR0013`, `CHR0022`, `CHR0031`, `CHR0032` |
47
47
 
48
+ Scenario read-model seeding/absence, synchronous handler exception propagation,
49
+ and the `EventContext.From` UTC default are verified separately at **Chronicle
50
+ 19.46.0**; they require **19.46.0+**. See the source paths in
51
+ [references/specifications.md](references/specifications.md).
52
+
48
53
  Reverify product sources before claiming support for another version.
49
54
 
50
55
  > **There is no `ReactorSideEffect` type.** It does not exist in Chronicle, in
@@ -363,7 +368,17 @@ when they serve that concern.
363
368
 
364
369
  Start with `Specification` plus a direct call for pure reactor decisions; add `ReactorScenario<TReactor>` only where invocation, dependency wiring or side effects contribute proof. Specify the contract, not only the happy path: field lineage, contract-authorized filtering, repeated delivery, and unrelated events.
365
370
 
366
- Read [references/specifications.md](references/specifications.md) when writing or reviewing reactor specifications.
371
+ In Chronicle **19.46.0+**, a handler's unseeded read-model parameter throws
372
+ `ReadModelNotSeededForReactorScenario`, even if nullable. Seed it through
373
+ `Given.ForEventSourceId(id).ReadModel(instance)`, or declare intentional absence
374
+ with `.NoReadModel<T>()`. Use the resolved read-model key; `Given` events do not
375
+ build read models, including passive ones. A synchronous handler throw surfaces
376
+ the handler's exception rather than a reflection wrapper, and the scenario's
377
+ `EventContext.Occurred` defaults to UTC.
378
+
379
+ Read [references/specifications.md](references/specifications.md) before writing
380
+ or reviewing reactor specifications; it shows both setup alternatives and the
381
+ scope of these scenario-only guards.
367
382
 
368
383
  ## Verify
369
384
 
@@ -1,3 +1,12 @@
1
+ # Reactor specifications
2
+
3
+ ## Contents
4
+
5
+ - Specifications
6
+ - Read-model setup — Chronicle 19.46.0+
7
+ - Exceptions and occurrence time — Chronicle 19.46.0+
8
+ - Contract coverage
9
+
1
10
  ## Specifications
2
11
 
3
12
  Start with `Specification` plus a direct call for pure reactor decisions,
@@ -9,6 +18,82 @@ non-event side effects; for handlers that return events, assert the resulting
9
18
  appends through the scenario's event store. Cover the replay path separately
10
19
  when the reactor has `[OnceOnly]` or `[Replay]` handlers.
11
20
 
21
+ ## Read-model setup — Chronicle 19.46.0+
22
+
23
+ Class-based `ReactorScenario<TReactor>` does not build read models from `Given`
24
+ events, **including passive models**. Seed a pre-built instance in `Establish()`:
25
+
26
+ ```csharp
27
+ void Establish() => _scenario.Given.ForEventSourceId(_id).ReadModel(_order);
28
+ async Task Because() => await _scenario.Given.ForEventSource(_id).Events(_orderPlaced);
29
+ ```
30
+
31
+ For a **separate absence specification**, use this setup instead, so an `Order?`
32
+ handler parameter deliberately receives `null`:
33
+
34
+ ```csharp
35
+ void Establish() => _scenario.Given.ForEventSourceId(_id).NoReadModel<Order>();
36
+ ```
37
+
38
+ The builders live in `Cratis.Chronicle.Testing.Reactors`. Exact signatures on
39
+ `ReactorSourceGivenBuilder<TReactor>` (`where TReactor : class, IReactor`):
40
+
41
+ ```csharp
42
+ public ReactorSourceGivenBuilder<TReactor> ReadModel<TReadModel>(TReadModel readModel)
43
+ where TReadModel : class;
44
+ public ReactorSourceGivenBuilder<TReactor> NoReadModel<TReadModel>()
45
+ where TReadModel : class;
46
+ public Task Events(params object[] events);
47
+ ```
48
+
49
+ Both `Given.ForEventSource(EventSourceId)` and
50
+ `Given.ForEventSourceId(EventSourceId)` return this builder. Seed or declare
51
+ absence using the triggering source id by default, or the key returned by
52
+ `ICanResolveReadModelKey.Resolve` when implemented. Declarations are shared
53
+ across the scenario's builders, match the **exact type and key**, and do not
54
+ remove a seeded instance. Do not declare absence to hide forgotten setup.
55
+
56
+ If a recognized read-model parameter resolves to `null` without declared
57
+ absence, `Events(...)` throws `ReadModelNotSeededForReactorScenario` from
58
+ `Cratis.Chronicle.Testing.Reactors`, **even for nullable parameters**. Its message
59
+ names the full read-model type and key, tells you to seed via `ReadModel(...)`
60
+ or declare `NoReadModel<T>()`, and lists other seeded keys for that type when
61
+ any exist. Check the key first if the fixture already seeds that type. This
62
+ guard applies only when the scenario uses `EventStoreForTesting` (including an
63
+ explicit instance); an explicitly supplied store of another type and production
64
+ read-model resolution are unchanged.
65
+
66
+ ## Exceptions and occurrence time — Chronicle 19.46.0+
67
+
68
+ `Events(...)` rethrows a synchronously throwing handler's **own exception**,
69
+ unwrapping the `TargetInvocationException` previously surfaced by reflection,
70
+ and preserves the handler stack. Capture with
71
+ `await Catch.Exception(() => _scenario.Given.ForEventSource(_id).Events(_orderPlaced))`
72
+ and assert the handler's exception type, not a reflection wrapper.
73
+
74
+ `EventContext.From(...)` defaults omitted `occurred` to `DateTimeOffset.UtcNow`
75
+ (previously `DateTimeOffset.Now`). `ReactorScenario` uses this helper, as do
76
+ `EventContext.Empty` and `EmptyWithEventSourceId(...)`. Explicit timestamps keep
77
+ their supplied offset; the positional constructor does not default `Occurred`.
78
+ For deterministic time-dependent plain-call specs, supply a fixed occurrence
79
+ time rather than asserting an exact current instant.
80
+
81
+ Verified against **Chronicle tag `v19.46.0`**:
82
+
83
+ - `Source/Clients/Testing/Reactors/ReactorSourceGivenBuilder.cs` and
84
+ `ReactorScenarioGivenBuilder.cs`: seeding, absence and builder signatures.
85
+ - `Source/Clients/Testing/Reactors/ScenarioReactorMethodArgumentsResolver.cs` and
86
+ `ReadModelNotSeededForReactorScenario.cs`: guard scope, resolved key and message.
87
+ - `Source/Clients/Testing/Reactors/ReactorScenario.cs`: synchronous exception
88
+ unwrapping and context creation.
89
+ - `Source/Clients/DotNET/Events/EventContext.cs`: UTC default and explicit time.
90
+ - `Source/Clients/Testing.Specs/Reactors/for_ReactorScenario/when_resolving_read_model_dependencies/`:
91
+ seeded, absent, nullable, passive, custom-key and explicit-store examples.
92
+ - `Source/Clients/Testing.Specs/Reactors/for_ReactorScenario/when_a_reactor_handler_throws_synchronously.cs`
93
+ and `when_a_reactor_reads_the_occurrence_time.cs`: handler exception and UTC specs.
94
+
95
+ ## Contract coverage
96
+
12
97
  Specify the contract, not only the happy path:
13
98
 
14
99
  - **Field lineage.** For each returned command or event, a specification asserts
@@ -78,10 +78,14 @@ Do not assume `string.Empty`, `0`, or `Guid.Empty` is universally invalid.
78
78
 
79
79
  ## Let a concept generate its own value
80
80
 
81
- Check the Cratis.Fundamentals version the project resolves first. Arc and
82
- Chronicle can bring an older one; when it is below 7.23.0, add a direct
83
- `Cratis.Fundamentals` 7.23.0 or later package reference, or keep the existing
84
- `static New()` (without the interface) until you can.
81
+ Check the Cratis.Fundamentals version the project resolves first. Arc first
82
+ pinned **7.23.0 in v22.66.0** (also verified at v22.68.0); the older-version
83
+ caveat is not a reason to add a direct reference for Arc 22.66.0+ when it already
84
+ resolves 7.23.0 or later. Chronicle **v19.43.0** still pins **7.22.0** in
85
+ `Directory.Packages.props`. For older Arc or Chronicle-only applications, when
86
+ the resolved version is below 7.23.0, add a direct `Cratis.Fundamentals` 7.23.0
87
+ or later package reference, or keep the existing `static New()` (without the
88
+ interface) until you can.
85
89
 
86
90
  When the system, not the caller, creates a value, the concept declares how by
87
91
  implementing `IGeneratable<TSelf>` (`Cratis.Concepts`, Cratis.Fundamentals
@@ -123,7 +127,11 @@ Do not generate when:
123
127
 
124
128
  In specifications, construct the concept from a known value
125
129
  (`new <ConceptName>(Guid.Parse("…"))`) and assert against it. Do not seed or
126
- replace the generator. Guide: `/fundamentals/csharp/generating-values/`.
130
+ replace the generator. In Arc **22.66.0+**, let a server-owned new identifier
131
+ reach `Handle()` through `[GeneratedValue]` rather than calling `New()` inside
132
+ it; see `cratis-arc-command` for declaration and migration, and
133
+ `cratis-specifications-csharp` for named scenario pins (Arc **22.67.0+**).
134
+ Guide: `/fundamentals/csharp/generating-values/`.
127
135
 
128
136
  ## Create a Guid-backed Chronicle stream identity
129
137
 
@@ -34,8 +34,9 @@ identity, effect and pending-work contract at that point; do not postpone it to
34
34
  planner audits through v7. Deliver that scope as authorized gap-fill
35
35
  (`references/realization-and-gap-fill.md`).
36
36
  - Reaction `runs as system role "<Role>"` declares an invocation actor (ESM v10).
37
- Without it the invoke has no caller. CLI 3.41.0's 4.114.0 compiler rejects the
38
- new syntax (PLAY0137). Checked model/diagnostics: captures-and-reactions
37
+ Without it the invoke has no caller. Historical CLI 3.41.0's 4.114.0 compiler
38
+ rejected the syntax (PLAY0137); current CLI 3.46.1 instead refuses ESM v10
39
+ at render admission (CLI-RENDER-004). Checked model/diagnostics: captures-and-reactions
39
40
  `references/invocation-identity.md`.
40
41
 
41
42
  ## Effect guarantees and recovery traps
@@ -11,8 +11,8 @@ technique: partial work stays visible and nothing is skipped because it looked r
11
11
 
12
12
  Result is `complete` only when every column is filled or says why it does not apply (for example
13
13
  "direct effect: immediate, internal, cannot fail"). `open` names the unresolved decision and who
14
- answers. `blocked` names the actual capability gap (for example CLI 3.41.0's
15
- older compiler cannot parse a declared ESM v10 reaction actor) with intended behavior.
14
+ answers. `blocked` names the actual capability gap (for example current CLI
15
+ 3.46.1 refuses ESM v10 at render admission with CLI-RENDER-004) with intended behavior.
16
16
 
17
17
  ## Per translation (capture or translator reaction)
18
18
  | Source and type | Location | Correlation | Field dispositions | Target fields traced | Dedup and ordering | Trust boundary | Recovery owner | Named cases | Result |
@@ -71,8 +71,10 @@ When a todo list is not needed: the effect is immediate, internal and cannot fai
71
71
  Screenplay 4.125.0 declares the actor with reaction-level `runs as system role
72
72
  "<Role>"` (ESM v10); without it a gated invocation has no caller and denies.
73
73
  `given caller` is not an invocation actor. Role requirements remain enforced;
74
- claim conditions for this system principal remain unknown. CLI 3.41.0's older
75
- compiler rejects the syntax (PLAY0137), and Stage 4.51.3 admits through v7 only.
74
+ claim conditions for this system principal remain unknown. Historical CLI 3.41.0's
75
+ older compiler rejected the syntax (PLAY0137). Current CLI 3.46.1 bundles
76
+ Screenplay 4.130.0 / Stage 4.52.0 and refuses v10 at render admission
77
+ (CLI-RENDER-004; toolchain `references/versions.md`).
76
78
  Use the checked model in captures-and-reactions `references/invocation-identity.md`.
77
79
  Arc gap-fill uses `[ExecuteCommandsAsSystem("<role>")]` for returned commands,
78
80
  not identity inferred from prose or an imperative pipeline call.
@@ -40,7 +40,9 @@ Changes in this version: the examples are complete fenced documents that compile
40
40
  example's anonymous-booking spec gained the `given caller` PLAY0389 requires; the todo-list
41
41
  example's list query, `reads` and retry sweep moved to marked design-only excerpts so the core
42
42
  binds); tool-qualified verdict text; the trusted-actor text now names `[ExecuteCommandsAsSystem]`
43
- (Arc v20.56.0). Current actor syntax was reverified at Screenplay v4.125.0
43
+ (Arc v20.56.0). Actor syntax was reverified at Screenplay v4.125.0
44
44
  `reactions.md`, `ReactionIdentityParser.cs`, `ReactionRefusalValidator.cs` and the
45
- binder's v10 selection; the checked scenario passes reference execution. CLI
46
- 3.41.0's older parser and Stage 4.51.3's v7 planner remain target limits.
45
+ binder's v10 selection; the checked scenario passed reference execution. CLI
46
+ 3.41.0's older parser and Stage 4.51.3's v7 planner were historical target limits.
47
+ Current CLI 3.46.1 bundles Screenplay 4.130.0 / Stage 4.52.0 and still refuses
48
+ v10 at render admission (CLI-RENDER-004; toolchain `references/versions.md`).
@@ -75,8 +75,9 @@ reference invocation's authenticated system principal with exactly those roles;
75
75
  Read [invocation identity](references/invocation-identity.md) for the checked
76
76
  scenario, claim/unknown semantics and PLAY0647–0652/0557 diagnostics.
77
77
 
78
- CLI 3.41.0's 4.114.0 compiler rejects this source with PLAY0137; its version gate
79
- and Stage 4.51.3 planner admit only through v7. Target realization is gap-fill,
78
+ Historical CLI 3.41.0's 4.114.0 compiler rejected this source with PLAY0137.
79
+ Current CLI 3.46.1 bundles Screenplay 4.130.0 / Stage 4.52.0; its render version
80
+ gate still admits only through v7 (CLI-RENDER-004 for v10). Target realization is gap-fill,
80
81
  using Arc `[ExecuteCommandsAsSystem]` for returned commands. Without that attribute
81
82
  Arc supplies no principal and `[Authorize]`/`[Roles]` deny. Each invoked command
82
83
  is atomic, not the whole cascade; earlier accepted facts remain after a later refusal.
@@ -69,11 +69,12 @@ module Work
69
69
  denial; opaque/unknown cases are not guessed. `--check privilege` (PLAY0652)
70
70
  reviews whether event producers require every elevated role, not runtime safety.
71
71
 
72
- CLI 3.41.0 bundles Screenplay 4.114.0, which does not parse `runs as`: the example's
73
- `cratis screenplay validate --executable --warnings-as-errors` returns PLAY0137,
74
- exit 5. It cannot render this source. Its render version gate separately admits
75
- only through v7 (CLI-RENDER-004 for newer admitted compiler output), while Stage
76
- 4.51.3's direct planner audits through v7 and refuses v10 with STAGE-ESM-016.
72
+ Historical CLI 3.41.0 bundled Screenplay 4.114.0, which did not parse `runs as`:
73
+ the example's `cratis screenplay validate --executable --warnings-as-errors`
74
+ returned PLAY0137, exit 5. This is not a current parser claim. Current CLI 3.46.1
75
+ bundles Screenplay 4.130.0 but its render gate still admits only through v7
76
+ (CLI-RENDER-004 for v10); bundled Stage 4.52.0's direct planner audits through
77
+ v7 and refuses v10 with STAGE-ESM-016 (`references/versions.md` in toolchain).
77
78
  Clock/application-trigger identities have no Stage Arc realization either.
78
79
  Use authorized gap-fill with the model as contract; returned-command Arc identity
79
80
  uses `[ExecuteCommandsAsSystem]`, never permission inferred from a model header.
@@ -19,7 +19,7 @@ specification IssuingTheWeeklyDigest
19
19
 
20
20
  Under `cratis` before 3.28.2 these actions parsed and were checked against the application
21
21
  but bound to nothing (`PLAY0268` names ESM v6, decision 0022); on 3.28.2 they bind.
22
- Current standalone 4.125.0 executes admitted scenarios through `screenplay test`
22
+ Standalone 4.125.0 (checked; current pins in the toolchain `versions.md`) executes admitted scenarios through `screenplay test`
23
23
  or MCP `run-specifications`; source compilation alone is authored evidence.
24
24
  Clock rules: the clock is UTC and exact; an
25
25
  occurrence fires once when it is **due after `given clock` and at or before