@cratis/pi 7.34.1 → 7.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/package/corpus/agents/security-reviewer.md +1 -0
  2. package/package/corpus/agents/spec-writer.md +1 -1
  3. package/package/corpus/harnesses/pi/extensions/shared/rules.ts +3 -1
  4. package/package/corpus/hooks/scripts/cratis-nuget-pins.txt +7 -6
  5. package/package/corpus/hooks/scripts/cratis-release-notes-reviewed.cjs +7 -6
  6. package/package/corpus/hooks/scripts/cratis-store-mutations.json +7 -3
  7. package/package/corpus/product-sync.json +896 -0
  8. package/package/corpus/prompts/add-business-rule.prompt.md +1 -1
  9. package/package/corpus/prompts/ship-changes.prompt.md +2 -2
  10. package/package/corpus/rules/ai-distribution.md +31 -3
  11. package/package/corpus/rules/application-profile.md +2 -1
  12. package/package/corpus/rules/components.md +66 -7
  13. package/package/corpus/rules/concepts.md +11 -3
  14. package/package/corpus/rules/csharp.md +3 -1
  15. package/package/corpus/rules/dialogs.md +9 -0
  16. package/package/corpus/rules/efcore.md +1 -1
  17. package/package/corpus/rules/elixir.md +49 -0
  18. package/package/corpus/rules/framework.md +5 -3
  19. package/package/corpus/rules/general.md +1 -0
  20. package/package/corpus/rules/glossary.md +4 -4
  21. package/package/corpus/rules/go-engineering.md +60 -0
  22. package/package/corpus/rules/go.md +115 -0
  23. package/package/corpus/rules/java.md +5 -2
  24. package/package/corpus/rules/kotlin.md +7 -5
  25. package/package/corpus/rules/orleans.md +65 -9
  26. package/package/corpus/rules/profiles.md +43 -6
  27. package/package/corpus/rules/pull-requests.md +3 -3
  28. package/package/corpus/rules/react.md +8 -1
  29. package/package/corpus/rules/reactors.md +27 -3
  30. package/package/corpus/rules/specs.csharp.md +11 -4
  31. package/package/corpus/rules/specs.scenarios.csharp.md +35 -4
  32. package/package/corpus/rules/specs.typescript.md +2 -1
  33. package/package/corpus/rules/storybook.md +1 -1
  34. package/package/corpus/rules/typescript.md +1 -1
  35. package/package/corpus/rules/vertical-slices.md +37 -5
  36. package/package/corpus/skills/cratis-application-react-specifications/SKILL.md +4 -2
  37. package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +18 -8
  38. package/package/corpus/skills/cratis-application-slice-specifications/references/query-and-decision-scenarios.md +65 -0
  39. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +53 -57
  40. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +38 -42
  41. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +18 -7
  42. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authproxy.md +30 -0
  43. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +12 -9
  44. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +9 -9
  45. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/observable-emission-guards.md +8 -5
  46. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +20 -4
  47. package/package/corpus/skills/cratis-arc-command/SKILL.md +32 -10
  48. package/package/corpus/skills/cratis-arc-command/references/command-result.md +12 -5
  49. package/package/corpus/skills/cratis-arc-command/references/decisions-and-routing.md +95 -0
  50. package/package/corpus/skills/cratis-arc-command/references/generated-values.md +3 -2
  51. package/package/corpus/skills/cratis-arc-command/references/handler-shapes.md +8 -8
  52. package/package/corpus/skills/cratis-arc-command/references/proxy-generation.md +22 -3
  53. package/package/corpus/skills/cratis-arc-command/references/read-model-injection.md +13 -1
  54. package/package/corpus/skills/cratis-arc-command-execution/SKILL.md +19 -9
  55. package/package/corpus/skills/cratis-arc-command-kotlin/SKILL.md +44 -18
  56. package/package/corpus/skills/cratis-arc-command-kotlin/references/read-model-naming.md +29 -0
  57. package/package/corpus/skills/cratis-arc-command-operation/SKILL.md +10 -9
  58. package/package/corpus/skills/cratis-arc-command-operation/references/execution-and-recovery.md +2 -2
  59. package/package/corpus/skills/cratis-arc-command-operation/references/testing.md +4 -3
  60. package/package/corpus/skills/cratis-arc-command-typescript/SKILL.md +42 -22
  61. package/package/corpus/skills/cratis-arc-command-typescript/references/chronicle-events.md +36 -5
  62. package/package/corpus/skills/cratis-arc-command-typescript/references/command-operations.md +114 -0
  63. package/package/corpus/skills/cratis-arc-command-typescript/references/hosting-and-storage.md +37 -16
  64. package/package/corpus/skills/cratis-arc-command-typescript/references/identity-and-introspection.md +88 -0
  65. package/package/corpus/skills/cratis-arc-command-typescript/references/observability-and-host-scopes.md +25 -0
  66. package/package/corpus/skills/cratis-arc-command-typescript/references/proxy-generation.md +35 -4
  67. package/package/corpus/skills/cratis-arc-command-typescript/references/slice-layout.md +11 -0
  68. package/package/corpus/skills/cratis-arc-command-typescript/verification.json +8 -1
  69. package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +49 -19
  70. package/package/corpus/skills/cratis-arc-ef-core-migration/SKILL.md +12 -6
  71. package/package/corpus/skills/cratis-arc-go/SKILL.md +148 -0
  72. package/package/corpus/skills/cratis-arc-go/references/queries.md +80 -0
  73. package/package/corpus/skills/cratis-arc-hosting/SKILL.md +96 -0
  74. package/package/corpus/skills/cratis-arc-hosting/references/integrations-and-publish.md +80 -0
  75. package/package/corpus/skills/cratis-arc-identity-and-tenancy-kotlin/SKILL.md +137 -0
  76. package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +20 -2
  77. package/package/corpus/skills/cratis-arc-query-kotlin/SKILL.md +26 -6
  78. package/package/corpus/skills/cratis-arc-query-paging/SKILL.md +25 -20
  79. package/package/corpus/skills/cratis-arc-query-typescript/SKILL.md +32 -6
  80. package/package/corpus/skills/cratis-arc-query-typescript/references/storage-observation-and-compliance.md +66 -0
  81. package/package/corpus/skills/cratis-arc-query-typescript/verification.json +5 -1
  82. package/package/corpus/skills/cratis-arc-react-page/SKILL.md +24 -13
  83. package/package/corpus/skills/cratis-arc-react-page/references/data-page.md +20 -10
  84. package/package/corpus/skills/cratis-arc-react-page/references/data-tables.md +17 -3
  85. package/package/corpus/skills/cratis-arc-react-page/references/dialogs.md +14 -3
  86. package/package/corpus/skills/cratis-arc-react-page/references/from-screenplay.md +6 -1
  87. package/package/corpus/skills/cratis-arc-react-page/references/provenance.md +1 -1
  88. package/package/corpus/skills/cratis-arc-react-page/references/queries-and-commands.md +11 -1
  89. package/package/corpus/skills/cratis-arc-react-page/verification.json +6 -1
  90. package/package/corpus/skills/cratis-arc-spring-data-kotlin/SKILL.md +163 -0
  91. package/package/corpus/skills/cratis-arc-testing-kotlin/SKILL.md +131 -0
  92. package/package/corpus/skills/cratis-arc-validation-kotlin/SKILL.md +44 -11
  93. package/package/corpus/skills/cratis-arc-validation-typescript/SKILL.md +5 -3
  94. package/package/corpus/skills/cratis-chronicle-cli-operations/SKILL.md +79 -25
  95. package/package/corpus/skills/cratis-chronicle-cli-operations/references/command-effects.md +57 -0
  96. package/package/corpus/skills/cratis-chronicle-client-dotnet/SKILL.md +87 -99
  97. package/package/corpus/skills/cratis-chronicle-client-dotnet/references/additional-surfaces.md +65 -0
  98. package/package/corpus/skills/cratis-chronicle-client-dotnet/references/client-construction.md +42 -0
  99. package/package/corpus/skills/cratis-chronicle-client-dotnet/references/compatibility-check.md +12 -0
  100. package/package/corpus/skills/cratis-chronicle-client-dotnet/references/discovery.md +43 -19
  101. package/package/corpus/skills/cratis-chronicle-client-dotnet/references/hosting.md +29 -1
  102. package/package/corpus/skills/cratis-chronicle-client-elixir/SKILL.md +158 -162
  103. package/package/corpus/skills/cratis-chronicle-client-elixir/references/append-api.md +36 -15
  104. package/package/corpus/skills/cratis-chronicle-client-elixir/references/compliance.md +32 -0
  105. package/package/corpus/skills/cratis-chronicle-client-elixir/references/connections.md +56 -0
  106. package/package/corpus/skills/cratis-chronicle-client-elixir/references/event-sources.md +64 -0
  107. package/package/corpus/skills/cratis-chronicle-client-elixir/references/pitfalls.md +25 -0
  108. package/package/corpus/skills/cratis-chronicle-client-elixir/references/projections.md +48 -0
  109. package/package/corpus/skills/cratis-chronicle-client-elixir/references/upgrading.md +34 -0
  110. package/package/corpus/skills/cratis-chronicle-client-elixir/references/variants.md +13 -5
  111. package/package/corpus/skills/cratis-chronicle-client-elixir/verification.json +23 -0
  112. package/package/corpus/skills/cratis-chronicle-client-go/SKILL.md +162 -0
  113. package/package/corpus/skills/cratis-chronicle-client-go/references/projections.md +73 -0
  114. package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +87 -88
  115. package/package/corpus/skills/cratis-chronicle-client-kotlin/references/connection-lifecycle.md +13 -0
  116. package/package/corpus/skills/cratis-chronicle-client-kotlin/references/discovery-and-registration.md +14 -4
  117. package/package/corpus/skills/cratis-chronicle-client-kotlin/references/event-sources.md +65 -0
  118. package/package/corpus/skills/cratis-chronicle-client-kotlin/references/options.md +29 -0
  119. package/package/corpus/skills/cratis-chronicle-client-kotlin/references/spring-boot.md +13 -0
  120. package/package/corpus/skills/cratis-chronicle-client-kotlin/references/variants.md +5 -4
  121. package/package/corpus/skills/cratis-chronicle-client-typescript/SKILL.md +165 -156
  122. package/package/corpus/skills/cratis-chronicle-client-typescript/references/read-models-and-projections.md +54 -11
  123. package/package/corpus/skills/cratis-chronicle-client-typescript/references/testing.md +126 -0
  124. package/package/corpus/skills/cratis-chronicle-client-typescript/references/variants.md +3 -5
  125. package/package/corpus/skills/cratis-chronicle-client-typescript/verification.json +19 -0
  126. package/package/corpus/skills/cratis-chronicle-compliance/SKILL.md +36 -13
  127. package/package/corpus/skills/cratis-chronicle-decision-reads/SKILL.md +110 -0
  128. package/package/corpus/skills/cratis-chronicle-decision-reads/references/admission-and-ownership.md +57 -0
  129. package/package/corpus/skills/cratis-chronicle-decision-reads/verification.json +12 -0
  130. package/package/corpus/skills/cratis-chronicle-event-constraints/SKILL.md +58 -19
  131. package/package/corpus/skills/cratis-chronicle-event-constraints/references/closing-streams.md +73 -0
  132. package/package/corpus/skills/cratis-chronicle-event-modeling/SKILL.md +32 -3
  133. package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +18 -5
  134. package/package/corpus/skills/cratis-chronicle-event-type-migration/SKILL.md +32 -2
  135. package/package/corpus/skills/cratis-chronicle-hosting/SKILL.md +134 -0
  136. package/package/corpus/skills/cratis-chronicle-hosting/references/operations.md +89 -0
  137. package/package/corpus/skills/cratis-chronicle-hosting/verification.json +14 -0
  138. package/package/corpus/skills/cratis-chronicle-mcp-inspection/SKILL.md +93 -66
  139. package/package/corpus/skills/cratis-chronicle-mcp-inspection/references/blocked-tools.md +21 -7
  140. package/package/corpus/skills/cratis-chronicle-mcp-inspection/references/deployment.md +77 -0
  141. package/package/corpus/skills/cratis-chronicle-mcp-inspection/references/observational-tools.md +46 -4
  142. package/package/corpus/skills/cratis-chronicle-multi-tenancy/SKILL.md +15 -1
  143. package/package/corpus/skills/cratis-chronicle-projection/SKILL.md +44 -10
  144. package/package/corpus/skills/cratis-chronicle-projection/references/fluent-builder.md +7 -1
  145. package/package/corpus/skills/cratis-chronicle-projection/references/model-bound-attributes.md +8 -0
  146. package/package/corpus/skills/cratis-chronicle-reactor/SKILL.md +85 -105
  147. package/package/corpus/skills/cratis-chronicle-reactor/references/specifications.md +20 -1
  148. package/package/corpus/skills/cratis-chronicle-read-model/SKILL.md +2 -2
  149. package/package/corpus/skills/cratis-chronicle-read-model/references/queries.md +35 -16
  150. package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +15 -3
  151. package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +23 -4
  152. package/package/corpus/skills/cratis-chronicle-web-workbench/SKILL.md +40 -15
  153. package/package/corpus/skills/cratis-cli-terminal-workbench/SKILL.md +20 -6
  154. package/package/corpus/skills/cratis-code-review/SKILL.md +25 -3
  155. package/package/corpus/skills/cratis-components-accessibility/SKILL.md +20 -4
  156. package/package/corpus/skills/cratis-components-schema-editor/SKILL.md +40 -28
  157. package/package/corpus/skills/cratis-components-schema-editor/references/tree-layout.md +62 -0
  158. package/package/corpus/skills/cratis-components-schema-editor/verification.json +14 -0
  159. package/package/corpus/skills/cratis-components-stepper-command-dialog/SKILL.md +16 -8
  160. package/package/corpus/skills/cratis-components-styling/SKILL.md +57 -20
  161. package/package/corpus/skills/cratis-components-styling/verification.json +13 -0
  162. package/package/corpus/skills/cratis-components-toolbar/SKILL.md +37 -7
  163. package/package/corpus/skills/cratis-components-toolbar/verification.json +12 -0
  164. package/package/corpus/skills/cratis-engineering-csharp-conventions/SKILL.md +6 -0
  165. package/package/corpus/skills/cratis-engineering-csharp-conventions/references/exceptions-logging-and-di.md +5 -3
  166. package/package/corpus/skills/cratis-fundamentals-concept/SKILL.md +19 -6
  167. package/package/corpus/skills/cratis-fundamentals-concept/references/typescript-concepts.md +97 -0
  168. package/package/corpus/skills/cratis-fundamentals-concept/verification.json +4 -1
  169. package/package/corpus/skills/cratis-fundamentals-concept-go/SKILL.md +112 -0
  170. package/package/corpus/skills/cratis-fundamentals-type-discovery/SKILL.md +54 -17
  171. package/package/corpus/skills/cratis-fundamentals-type-discovery/verification.json +14 -0
  172. package/package/corpus/skills/cratis-lens-browser-extension/SKILL.md +55 -28
  173. package/package/corpus/skills/cratis-performance-review/SKILL.md +5 -2
  174. package/package/corpus/skills/cratis-release-notes/SKILL.md +3 -1
  175. package/package/corpus/skills/cratis-screenplay-automations-and-translations/SKILL.md +5 -4
  176. package/package/corpus/skills/cratis-screenplay-command-surface/SKILL.md +5 -6
  177. package/package/corpus/skills/cratis-screenplay-command-surface/references/compliance.md +24 -6
  178. package/package/corpus/skills/cratis-screenplay-command-surface/references/enum-numbers.md +4 -2
  179. package/package/corpus/skills/cratis-screenplay-command-surface/references/generated-values-and-responses.md +4 -1
  180. package/package/corpus/skills/cratis-screenplay-event-modeling/SKILL.md +3 -2
  181. package/package/corpus/skills/cratis-screenplay-event-modeling/references/parsed-not-runnable.md +2 -2
  182. package/package/corpus/skills/cratis-screenplay-legacy-extraction/SKILL.md +1 -0
  183. package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/generate-notes.md +34 -4
  184. package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +5 -3
  185. package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md +1 -1
  186. package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-tools.md +4 -2
  187. package/package/corpus/skills/cratis-screenplay-model-authoring/references/property-links.md +42 -3
  188. package/package/corpus/skills/cratis-screenplay-model-authoring/verification.json +3 -1
  189. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/gap-fill-handoff.md +3 -1
  190. package/package/corpus/skills/cratis-screenplay-scenario-coverage/SKILL.md +3 -1
  191. package/package/corpus/skills/cratis-screenplay-specifications/references/examples-and-routes.md +48 -0
  192. package/package/corpus/skills/cratis-screenplay-specifications/verification.json +3 -1
  193. package/package/corpus/skills/cratis-screenplay-streams-and-consistency/SKILL.md +4 -2
  194. package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/streams-example.md +5 -4
  195. package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +8 -6
  196. package/package/corpus/skills/cratis-screenplay-toolchain/references/cheat-sheet-example.md +2 -2
  197. package/package/corpus/skills/cratis-screenplay-toolchain/references/diagnostics.md +4 -2
  198. package/package/corpus/skills/cratis-screenplay-toolchain/references/doc-contradictions.md +1 -1
  199. package/package/corpus/skills/cratis-screenplay-toolchain/references/executable-subset.md +16 -11
  200. package/package/corpus/skills/cratis-screenplay-toolchain/references/renderable-subset.md +5 -2
  201. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +1329 -32
  202. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
  203. package/package/corpus/skills/cratis-screenplay-toolchain/references/verdicts.md +2 -2
  204. package/package/corpus/skills/cratis-screenplay-toolchain/references/versions.md +23 -7
  205. package/package/corpus/skills/cratis-screenplay-toolchain/verification.json +1 -1
  206. package/package/corpus/skills/cratis-screenplay-ui-composition/SKILL.md +2 -0
  207. package/package/corpus/skills/cratis-screenplay-ui-composition/references/screen-composition-contract.md +27 -9
  208. package/package/corpus/skills/cratis-security-review/SKILL.md +9 -3
  209. package/package/corpus/skills/cratis-security-review/references/arc-boundaries.md +59 -0
  210. package/package/corpus/skills/cratis-specification-by-example/SKILL.md +7 -0
  211. package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +43 -11
  212. package/package/corpus/skills/cratis-specifications-csharp/references/application-scenarios.md +16 -4
  213. package/package/corpus/skills/cratis-specifications-csharp/references/csharp-patterns.md +18 -5
  214. package/package/corpus/skills/cratis-specifications-csharp/references/integration-specs.md +21 -0
  215. package/package/corpus/skills/cratis-specifications-csharp/references/shared-lifecycle.md +34 -0
  216. package/package/corpus/skills/cratis-specifications-csharp/verification.json +13 -0
  217. package/package/corpus/skills/cratis-specifications-typescript/SKILL.md +14 -5
  218. package/package/corpus/skills/cratis-specifications-typescript/verification.json +12 -0
  219. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +8 -2
  220. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +6 -3
  221. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/rendered-application.md +10 -1
  222. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/scene-runtime.md +75 -0
  223. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/verification.json +5 -0
  224. package/package/corpus/skills/cratis-studio-mcp-safety-guidance/SKILL.md +14 -1
  225. package/package/profile-catalog.json +68 -1
  226. package/package.json +1 -1
  227. package/src/index.ts +3 -1
@@ -20,6 +20,7 @@ A command's `Handle()` is an ordinary method; with dependencies resolved by `Pro
20
20
  |---|---|---|
21
21
  | `Specification` (plain call) | `Handle()`, `Handle(providedValue)`, reducer logic or a reactor's decision as a method call; or an isolated class with mocked collaborators | **default** for a decision that is a pure function of its arguments |
22
22
  | `CommandScenario<TCommand>` | the real Arc command pipeline — authorization + validators + `Provide()` + `Handle()` + appended events | the pipeline, validation, authorization, or appended events matter (State Change slices) |
23
+ | `QueryScenario<TReadModel>` | query pipeline: argument conversion, authorization, validation, interceptors; `Observe` buffers emissions | query composition matters; real MongoDB observations still need infrastructure |
23
24
  | `EventScenario` | Chronicle-level append semantics, no command pipeline | constraint violations, raw stream sequencing/concurrency |
24
25
  | `ReadModelScenario<TReadModel>` | projection/reducer state from a sequence of events (auto-detects model-bound / fluent / reducer) | **default for projections**, which are declarative (State View slices) |
25
26
  | `ReactorScenario<TReactor>` | reactor handler invocation + side effects via mocked services | the reactor wiring and side effects (Automation / Translation slices) |
@@ -74,7 +75,7 @@ public class and_all_information_is_valid : Specification
74
75
  - `CommandScenario<TCommand>` exposes `Services` (an `IServiceCollection`), `Context`, `Execute(command[, ct])`, `Validate(command)`, `LastResult`, `Operations` and the operation assertions `ShouldHaveExecutedOperation<T>()` / `ShouldHaveCompensatedOperation<T>()` / `ShouldHaveNoOperationInvocations()`; with the Chronicle integration it also gains `Given`, `EventScenario`, `EventLog`, `EventSequence` and `AppendedEvents` as extension members. Everything else is configured through `Services` and asserted with extension methods.
75
76
  - **Event assertions are extension methods on the scenario** (from the Chronicle testing package), keyed by **command + event** type: `await _scenario.ShouldHaveAppendedEvent<TCommand, TEvent>(eventSourceId)` or the `(eventSourceId, Func<TEvent,bool> predicate)` overload; plus `await _scenario.ShouldHaveTailSequenceNumber<TCommand>(...)`. They return `Task`, so the fact is `async Task`.
76
77
  - **`CommandResult` assertions** (Arc extensions): `ShouldBeSuccessful()`, `ShouldNotBeSuccessful()`, `ShouldBeValid()`, `ShouldHaveValidationErrors()`, `ShouldHaveValidationErrorFor(message)`, `ShouldBeAuthorized()`, `ShouldNotBeAuthorized()`, `ShouldHaveExceptions()`/`ShouldNotHaveExceptions()`.
77
- - **Seed prior state through `_scenario.Given` or `_scenario.Services`.** `_scenario.Given.ForEventSource(id).Events(...)` materializes the seeded events into an **in-memory read-model dictionary** the harness answers keyed reads from — it never reaches the event log (see the false-greens list below). To populate the DCB read models the validator/`Provide()`/`Handle()` inject, substitute `IReadModels` and register it (`_scenario.Services.Replace(new ServiceDescriptor(typeof(IReadModels), mock))`, mocking `GetInstanceById(...)`) or register projections with `_scenario.Services.AddReadModels(...)`.
78
+ - **Seed prior state through `_scenario.Given` or `_scenario.Services`.** `_scenario.Given.ForEventSource(id).Events(...)` materializes the seeded events into an **in-memory read-model dictionary** the harness answers keyed reads from — in **legacy mode** it never reaches the event log (see the false-greens list below). To populate advisory read models the validator/`Provide()`/`Handle()` inject, substitute `IReadModels` and register it (`_scenario.Services.Replace(new ServiceDescriptor(typeof(IReadModels), mock))`, mocking `GetInstanceById(...)`) or register projections with `_scenario.Services.AddReadModels(...)`.
78
79
  - **Validator/`Provide()` dependencies:** register them in `_scenario.Services`; Arc testing discovers the concrete validator automatically. When several specs need different injected validator states, test rejected variants by instantiating the validator directly (per-scenario state can be order-sensitive under parallel xUnit).
79
80
  - **`Provide()`:** drive it through `CommandScenario` end-to-end; when the handler's decision is pure given provided data, test `Handle(providedValue)` directly as the default decision spec and keep the scenario for the wiring.
80
81
  - **Command operations:** use **cratis-arc-command-operation** for decision/adapter specs and real `CommandScenario` execution, forward failure, compensation, and Chronicle commit rejection; a direct `Handle()` does not execute operations.
@@ -99,14 +100,29 @@ Each of these passes while proving nothing. They are the reason the two-assertio
99
100
  1. **A discarded assertion `Task`.** The `IEventSequence` assertions `ShouldHaveAppendedEvent`/`ShouldHaveTailSequenceNumber` (Chronicle Testing) are genuinely `async`: in a `void [Fact]` that does not `await` them the exception lands on a `Task` nobody observes and the spec passes regardless of behavior, while the compiler's `CS4014` stays silent outside an `async` method. (The same-named `CommandScenario` overloads in Arc's Chronicle.Testing throw synchronously and only *return* a completed `Task`, so those fail even when discarded — but you cannot tell the two apart at the call site.) Make the fact `async Task` and `await` every `Task`-returning `Should*`. Chronicle's analyzer **`CHR0039` (`NonAwaitedAssertion`)** reports any discarded `Task`/`ValueTask`-returning `Should*` call from a Cratis testing package (Chronicle's and Arc's alike).
100
101
  2. **A dependency the spec forgot to seed.** When a validator's constructor, `Provide()` or `Handle()` asks for a read model that does not exist for the command's key, the validator is never built and no rule runs — yet the command is rejected (one validation result, reason **`DependencyUnavailable`**, HTTP 400), so `ShouldNotBeSuccessful()` passes and keeps passing with the rule it is named after deleted. `ShouldHaveValidationErrors()` **refuses** a result whose only errors carry that reason and tells you to seed the read model — or, when that rejection *is* the behavior under spec, assert it deliberately with `ShouldHaveValidationErrorBecauseOf(ValidationResultReason.DependencyUnavailable)`. A missing *service* (not a read model) surfaces as an exception instead (`ShouldHaveExceptions()`), and `ShouldNotBeSuccessful()` alone cannot tell the two apart from a genuine rejection.
101
102
  3. **`.Validate()` authorizes first.** `Validate(command)` runs the command filters, and authorization filters are ordered before validation. A validate-only spec on a `[Roles]`/`[Authorize]` command with no identity registered is denied for authorization — the rule it is named after never ran. Register the identity, or assert `ShouldNotBeAuthorized()` and say so in the name.
102
- 4. **Constraint preconditions go to the event log, not `CommandScenario.Given`.** `_scenario.Given.ForEventSource(id).Events(...)` on a `CommandScenario` seeds an **in-memory read-model materialization only**; nothing reaches the event log a `[Unique]`/`IConstraint` is enforced against, so a "second registration with the same name" spec passes because the first one was never appended. Seed constraint preconditions through **`EventScenario`**'s `Given`, and assert the violation by constraint **name** (`ShouldHaveConstraintViolationFor`) — the same helper exists on `CommandResult` and on `IAppendResult`.
103
- 5. **`GetInstances<T>` is not harness-backed.** Under `CommandScenario` the read-model harness answers keyed reads (`GetInstanceById`) from its seeded dictionary and forwards `GetInstances<T>`/`GetSnapshotsById`/`Watch` to the real client, which is not there. Code under spec that enumerates a read model needs a fake lookup registered in `Services`, not `Given`.
103
+ 4. **Legacy-mode constraint preconditions go to the event log, not `CommandScenario.Given`.** `_scenario.Given.ForEventSource(id).Events(...)` on a `CommandScenario` seeds an **in-memory read-model materialization only**; nothing reaches the event log a `[Unique]`/`IConstraint` is enforced against, so a "second registration with the same name" spec passes because the first one was never appended. Seed constraint preconditions through **`EventScenario`**'s `Given`, and assert the violation by constraint **name** (`ShouldHaveConstraintViolationFor`) — the same helper exists on `CommandResult` and on `IAppendResult`.
104
+ 5. **Legacy `GetInstances<T>` is not harness-backed.** Under legacy `CommandScenario` the read-model harness answers keyed reads (`GetInstanceById`) from its seeded dictionary and forwards `GetInstances<T>`/`GetSnapshotsById`/`Watch` to the real client, which is not there. Code under spec that enumerates a read model needs a fake lookup registered in `Services`, not `Given`.
104
105
  6. **One child is not a children spec.** `[ChildrenFrom<T>]` without `key:` identifies children by the **event source id**, so every child event on one stream upserts the same child and a one-child spec stays green with the key wrong. Drive **two** children with distinct keys and assert the count.
105
106
  7. **Independently derived expectations.** An expected value computed in the spec with the same expression as the code under spec agrees with it by construction. Write the expected literal.
106
107
 
107
108
  The `ReadModelScenario` and `EventScenario` equivalents of these live in the **cratis-chronicle-read-model-specifications** and **cratis-chronicle-event-specifications** skills.
108
109
 
109
- > **Validator-state order-sensitivity:** the command-scenario pipeline can cache enough state to make injected-validator branches order-sensitive when xUnit runs classes in parallel. Use `CommandScenario` for the valid path, test rejected state-variants by **instantiating the validator directly**, and — only for that validator-state case — put the command's specs in a **small xUnit `[Collection]`**. (This is distinct from the unique-value-collision rule below, where `[Collection]` is the *wrong* fix.)
110
+ > **Historical validator-state order-sensitivity:** observed in the 22.16-era harness. Arc 22.23.2 stopped replacing the process-wide provider; later releases isolated scenario stores and event-source services. Reproduce at 22.75.0 before applying the old direct-validator/small `[Collection]` workaround. No current reproduction is verified here.
111
+
112
+ #### Query and protected-decision modes (Arc v22.75.0)
113
+
114
+ `QueryScenario<TReadModel>` (`Cratis.Arc.Testing.Queries`) runs `Perform(nameof(Model.Method),
115
+ arguments?, paging?, sorting?)`; `Observe(...)` returns `QueryObservation.Initial`
116
+ and buffered `Next()`. Configure principal/policies in `Services`; schemes fail
117
+ closed without HTTP. Chronicle query `Given` can seed keyed models/events.
118
+
119
+ For guarded commands use `UseDecisionReads()` **before** seeding events. Given
120
+ state, guarded reads and appends share one real in-process log, unlike legacy
121
+ mode above. Pinned models cannot supply guards. Use `OnRoute(route)` and
122
+ `AppendConcurrently` to prove conflicts; `ShouldHaveCompletedStream` proves
123
+ completion and `DecisionReadAdmissions.ShouldAdmitDecisionReadsIn(assembly)`
124
+ checks admission. See **cratis-application-slice-specifications**,
125
+ `references/query-and-decision-scenarios.md`, for sources and limitations.
110
126
 
111
127
  ### `EventScenario` — constraints & append semantics
112
128
 
@@ -144,6 +160,14 @@ Use the named-parameter constructor when services/initial state are needed: `new
144
160
 
145
161
  **Bridging `IReadModels` into a `CommandScenario`** (current harness limitation): when a command handler injects `IReadModels` directly, build a focused `ReadModelScenario<T>`, seed it via `Given.ForEventSourceId(id).ReadModel(...)`, then register its read models into the command scenario — `_scenario.Services.AddSingleton(readModelScenario.ReadModels)`. Pass `scenario.ReadModels` into code under test for keyed `GetInstanceById` reads; for non-key searches/filters, register a fake lookup or an `IMongoCollection<T>` mock in the scenario services instead.
146
162
 
163
+ **Chronicle v19.50.0 scenario additions** (`Source/Clients/Testing/ReadModels/`):
164
+ `WithProjection(Action<IProjectionBuilderFor<T>>)` overrides discovery, including
165
+ reducers. `Given.ForEventSource(id).OnStream(eventSourceType, eventStreamType,
166
+ eventStreamId).Events(...)` seeds routing for filtered reducers; unrouted events
167
+ do not reach a differently filtered handler. Dispose scenarios in `Destroy()`.
168
+ Per-run `Defaults(clientArtifactsProvider, serviceProvider, options)` supports
169
+ `InMemoryClientArtifactsProvider` and `ExplicitArtifacts` without global discovery.
170
+
147
171
  **Skip discipline:** don't pre-emptively `[Fact(Skip=...)]` a read-model assertion — assume scalar `ConceptAs<string>`, enum, and identifier properties populate correctly; if one fails, investigate the projection. Only skip on a *reproduced* harness gap, and put the specific reason in the skip message.
148
172
 
149
173
  ### `ReactorScenario<TReactor>` — Automation / Translation
@@ -160,6 +184,13 @@ async Task Because() => await _scenario.Given.ForEventSource(_id).Events(new Aut
160
184
  [Fact] async Task should_notify() => await _service.Received(1).Notify("Jane Austen");
161
185
  ```
162
186
 
187
+ In **Chronicle v19.50.0** (`Source/Clients/Testing/Reactors/`), `OnStream` also
188
+ applies to class-based and fluent reactors. `ProducedEvents` carries payload and
189
+ destination; `ShouldHaveProduced<TEvent>(eventSourceId)` or the predicate
190
+ `((produced, @event) => ...)` verifies cross-stream routing. The non-generic
191
+ `ReactorScenario(reactorId, defaults)` exercises an explicitly registered reactor.
192
+ Dispose each scenario in `Destroy()`; await async assertions (CHR0039).
193
+
163
194
  #### Read-model setup — Chronicle 19.46.0+
164
195
 
165
196
  In a class-based `ReactorScenario<TReactor>`, `Given` events invoke the reactor;
@@ -78,7 +78,8 @@ export class an_events_command_response_value_handler {
78
78
  ```
79
79
 
80
80
  - Properties are public (not protected) — tests access them via `context.propertyName`.
81
- - Import `given` from the package root: `import { given } from '../../given';`.
81
+ - `given` is a package-local helper file, not a published export. Import it with the relative path to that file, for example `import { given } from '../../given';`; create it if absent. Fundamentals v7.23.0 constructs parameterless contexts; Arc v22.75.0 passes the Mocha Suite and binds callback `this` to it.
82
+ - Configure Vitest's setup file to call `chai.should()` and register `sinon-chai` and `chai-as-promised`. Load the reflection polyfill for metadata-dependent specifications.
82
83
  - Simple tests without shared setup don't need a reusable context.
83
84
 
84
85
  ## Simple Test Pattern (without context)
@@ -17,7 +17,7 @@ Good candidates: a presentational card/list-item/form-field with multiple visual
17
17
 
18
18
  ## Preview infrastructure (what you get for free)
19
19
 
20
- A configured `.storybook/preview` should wrap every story in `CratisComponentsProvider` (from `@cratis/components`) and import `@cratis/components/tokens`, `@cratis/components/styles` and (for the baseline look) `@cratis/components/theme` — the same three entries the app imports — so components render styled. With that in place:
20
+ A configured `.storybook/preview` should wrap every story in `CratisComponentsProvider` (from `@cratis/components`) with `toaster` when stories show notifications. Import one `index.css` containing `@import '@cratis/components/tokens'`, `@import '@cratis/components/styles'` and (for the baseline look) `@import '@cratis/components/theme'`, each terminated by a semicolon — the same entries as the app. CSS imports from TypeScript need ambient declarations on TypeScript 6; keeping the subpaths in CSS avoids TS2882. Mount only one toaster. With that in place:
21
21
 
22
22
  - **Auto prop tables** via `react-docgen-typescript` — a component's `interface` + per-prop JSDoc renders as a Docs prop table, and union props become `select` controls automatically. Keep a JSDoc comment on every prop. **`argTypes` is an override layer** (change control type, group under `table.category`, disable a control) — don't re-list every prop.
23
23
  - **Accessibility panel** (`@storybook/addon-a11y`) scans each story with axe-core. Treat new violations as defects.
@@ -121,7 +121,7 @@ All identifiers, comments, JSDoc, and string literals must use **American Englis
121
121
 
122
122
  ## Folder Structure
123
123
 
124
- - **Never give a `.tsx` the same basename as a `.ts` in the same folder.** The Arc proxy generator emits `<Slice>.ts` beside the slice's C#; a component named `<Slice>.tsx` next to it is **silently dropped from the program**: for one basename TypeScript keeps only the `.ts`, so the component's type errors are never reported and `tsc -b` stays green while the file is broken (reproduced on TypeScript 6.0.3 and 7.0.2 with `include: ["src/**/*"]`). Name the component for what it renders (`Listing.tsx`, `RegisterAuthor.tsx`), never for the slice, and consider a repository spec that fails on any `.tsx` with a sibling `.ts` of the same name.
124
+ - **Never give a `.tsx` the same basename as a `.ts` in the same folder.** The Arc proxy generator emits `<Slice>.ts` beside the slice's C#; a component named `<Slice>.tsx` next to it is **silently dropped from the program**: for one basename TypeScript keeps only the `.ts`, so the component's type errors are never reported and `tsc -b` stays green while the file is broken (reproduced on TypeScript 6.0.3 and 7.0.2 with `include: ["src/**/*"]`). At Arc v22.75.0 use `<CratisProxiesUseProxyFileSuffix>true</CratisProxiesUseProxyFileSuffix>` to emit `<Slice>.proxy.ts` with matching barrels; that is the clean generator fix. Otherwise name the component for what it renders (`Listing.tsx`, `RegisterAuthor.tsx`), never for the slice, and consider a repository spec that fails on any `.tsx` with a sibling `.ts` of the same name.
125
125
  - Do not prefix a file, component, type, or symbol with the name of its containing folder or the concept it belongs to. Instead, use folder structure to provide that context.
126
126
  - Favor functional folder structure over technical folder structure.
127
127
  - Group files by the feature or concept they belong to, not by their technical role.
@@ -88,7 +88,7 @@ The command carries input from the caller. `Handle()` is defined directly on the
88
88
  | Reusable value invariant (length, format, range) | `ConceptValidator<T>` on the concept type |
89
89
  | Command-input / cross-field / pre-handler rule (incl. injected read-model/service/identity checks) | `CommandValidator<TCommand>` with `RuleFor(...)` |
90
90
  | Handler needs fetched/computed data before it can build the event | `Provide()` — fetches the data; may validate it and short-circuit |
91
- | State-dependent rule that must hold **under concurrency** | inject the read model into `Handle()`, return `Result<TEvent, ValidationResult>` |
91
+ | State-dependent rule that must hold **under concurrency** | `IStreamReads`/`StreamDecision` for one stream, or `[ProtectedDecision]` + `DecisionRead<T>` for admitted projected state; plain injection is advisory |
92
92
  | Uniqueness | Chronicle `[Unique]` / `IConstraint` (race-safe) — not a read-model pre-check |
93
93
  | Genuinely exceptional failure (bug, missing infra) | `throw` a domain exception |
94
94
 
@@ -164,7 +164,7 @@ Return the event(s) directly — Arc appends them; never inject `IEventLog` to a
164
164
  | `(EventSourceId, TEvent)` | command decides the event source id (tuple order doesn't matter) |
165
165
  | `(TResponse, TEvent)` | a response value plus an event |
166
166
  | `IEnumerable<object>` | multiple events; `EventForEventSourceId(id, @event)` wrappers for cross-stream |
167
- | `Result<TEvent, ValidationResult>` | success event or a typed validation error (concurrency-sensitive rule) |
167
+ | `Result<TEvent, ValidationResult>` | success event or a typed validation error; does not itself guard concurrency |
168
168
  | `ICommandOperation` / `CommandOperations` | immediate server-side work after event enrollment, never the client response |
169
169
  | `void` | no event |
170
170
 
@@ -182,7 +182,25 @@ Three attributes scope an append broad→narrow, and each one *also* (only with
182
182
 
183
183
  Each attribute always tags the appended events (visible in `EventContext`). **Only when the command also carries `concurrency: true`** does each dimension join the server-side `ConcurrencyScope` (plus the event source id), with the optimistic strategy supplying the expected tail sequence number. A command **without** `concurrency: true` contributes no scope of its own, and the append falls to whatever concurrency strategy the event sequence is configured with — by default the optimistic one, scoped to the event source. So the attribute chooses *which dimensions* bound the check, not whether a check happens at all. Reach for it on high-contention state transitions where a stale read would let two commands both "win" — it narrows the boundary so appends that are genuinely independent stop colliding (concurrency failures surface on `CommandResult`/`AppendResult`). Adopt it deliberately per behavior, not as a blanket default.
184
184
 
185
- When the stream id is known only at runtime, implement `ICanProvideEventStreamId.GetEventStreamId()` instead of `[EventStreamId]`. ⚠️ Combining `ICanProvideEventStreamId` with a non-null `[EventStreamId]` attribute on the same command throws `AmbiguousEventStreamId` when that command is executed (the check runs while Arc builds the command context, not at startup) — pick one (or set the attribute value to `null` to defer to the interface).
185
+ At Arc v22.75.0, prefer `IStreamReads.ForCommand()` in `Provide()` and return
186
+ `StreamDecision.Append(...)` when deciding over one routed stream. For projected
187
+ state use `[ProtectedDecision]` + `DecisionRead<T>`; plain injection and keyed
188
+ reads are advisory, including in `Handle()`. Protected projections must be
189
+ Chronicle-admitted (direct source key, known events; not reducers, joins, children
190
+ or passive shapes); validators must be parameterless and certified by Arc.
191
+ Exact returned scopes replace automatic `concurrency: true` capture.
192
+
193
+ `[EventStreamId("{ReportingScopeId}:{Period}")]` resolves command property values
194
+ after validation; concepts supply their underlying values. An interface can
195
+ compute an id too. `AmbiguousEventStreamId` rejects any non-NotSet attribute
196
+ value combined with `ICanProvideEventStreamId`, including a template; pick one.
197
+ `[EventSource<TSource>(stream)]` selects a definition-backed route on commands and
198
+ aggregate roots. `EventRoute`/`IEventRoutes` reuse it; explicit wrapper routing
199
+ wins over command context. `[EventTag]`, `ICanProvideEventTags`, returned
200
+ `EventTags` and discovered `ICanProvideCommandEventTags` add tags to returned
201
+ events. `CompleteStream` runs after commit, not atomically with append, and closes
202
+ stream type/id across the log, not just one source. Read the Arc command skill's
203
+ `references/decisions-and-routing.md` for source-pinned contracts and limitations.
186
204
 
187
205
  ### Event-source id resolution order **[contract]**
188
206
 
@@ -232,6 +250,14 @@ public record AuthorRegistered(AuthorName Name);
232
250
 
233
251
  **[contract]** `[ReadModel]` records expose queries as `public static` methods; dependencies are plain method parameters (no `[FromServices]`). Default to model-bound projection shape; drop to fluent only when needed. For step-by-step creation, invoke the **cratis-chronicle-read-model** skill.
234
252
 
253
+ Use `[ReadModel(RequireQueryAttribute = true)]` with `[Query]` on intended
254
+ endpoints when public folds/helpers exist (ARC0026/ARCCHR0019). `[Query]` alone
255
+ does not hide siblings; private/compiler-generated statics are never queries.
256
+ A `CancellationToken` is supplied by Arc; concept arguments come from the caller,
257
+ not DI. Bad GET/QUERY argument conversions return 400. Single-instance MongoDB/EF
258
+ observations emit `default` on absence/deletion/filter exit: guard `hasData` and
259
+ `isReady` in React. Verified at Arc v22.75.0; see the read-model query reference.
260
+
235
261
  ### Query return shapes **[contract]**
236
262
 
237
263
  Stay within analyzer-supported shapes: the read model, `T?`, a collection/array/`IQueryable<T>` of it, `Task<...>` of those, `IAsyncEnumerable<T>`, or `ISubject<...>` of read model/collection.
@@ -268,7 +294,7 @@ query reference for versioned behavior and composition trade-offs.
268
294
 
269
295
  ### Choosing read-model access in command-side code **[convention]**
270
296
 
271
- Ladder, first that fits: (1) direct read-model (DCB) injection — only when keyed by the command's own event-source id; (2) a `[Passive]` projection; (3) `IReadModels.GetInstanceById<T>((EventSourceId)key)` for a different/derived key; (4) a materialized projection. A lookup *interface* is justified **only** for a genuine non-key search (e.g. find-by-email) — never to wrap a keyed `GetInstanceById`.
297
+ Ladder, first that fits: (1) advisory direct read-model injection — only when keyed by the command's own event-source id; (2) a `[Passive]` projection; (3) `IReadModels.GetInstanceById<T>((EventSourceId)key)` for a different/derived key; (4) a materialized projection. A lookup *interface* is justified **only** for a genuine non-key search (e.g. find-by-email) — never to wrap a keyed `GetInstanceById`.
272
298
 
273
299
  **[contract] Passive state is not sink state.** `IMongoCollection<T>` cannot
274
300
  compute a passive read model; any leftover documents are not maintained state.
@@ -366,7 +392,13 @@ Redaction is a system-event request that removes payload availability from the e
366
392
 
367
393
  Cross-slice access is **read-only and goes through Chronicle** — inject another slice's read model (validator/`Provide()`/`Handle()`), read by explicit key with `IReadModels.GetInstanceById`, mark a `[Passive]` projection for command-side decisions, or react to its events. **Never** instantiate or DI another slice's command/handler/service.
368
394
 
369
- **DCB (Dynamic Consistency Boundary):** a command's consistency boundary is exactly the state its validator/`Provide()`/`Handle()` reads — determined at runtime, not pre-declared. Pair with `concurrency: true` stream metadata when a stale read could let two commands both "win."
395
+ **DCB (Dynamic Consistency Boundary):** the boundary is the event state explicitly
396
+ enrolled in guarded decisions, not every model a command reads. Use stream
397
+ reads/exact scopes or `[ProtectedDecision]` + admitted `DecisionRead<T>`;
398
+ plain validator/`Provide()`/`Handle()` injection is advisory. Stream-level
399
+ `concurrency: true` scopes are another explicit boundary, not protection for an
400
+ arbitrary snapshot. Specify conflicts with `UseDecisionReads()` and actual seeded
401
+ events, not pinned models.
370
402
 
371
403
  ## Dialogs
372
404
 
@@ -132,8 +132,10 @@ behavior truly requires DOM events — do not make it the default. Avoid
132
132
  snapshots; they hide behavior and make refactors noisy.
133
133
 
134
134
  Cratis Components mount inside `CratisComponentsProvider`, so a specification
135
- that renders one for real must provide it, and jsdom needs a `ResizeObserver`
136
- stub for the table and split-pane components.
135
+ that renders one for real must provide it (enable `toaster` only when asserting
136
+ toasts). In jsdom, provide a `ResizeObserver` stub for any Components area that
137
+ observes size, including observable tables, Filter and toolbars. Source-checked
138
+ at Components v4.26.4 (`Source/**`); this is not a split-pane-only requirement.
137
139
 
138
140
  ## Async and time
139
141
 
@@ -23,12 +23,14 @@ This skill decides **when to add a scenario** and **what to cover**. The C# mech
23
23
 
24
24
  | Package | Version | Purpose |
25
25
  | --- | --- | --- |
26
- | `Cratis.Arc.Testing` | `22.16.0` | `CommandScenario<TCommand>` and the `CommandResult` assertions |
27
- | `Cratis.Arc.Chronicle.Testing` | `22.16.0` | `Given`, `EventLog`, `AppendedEvents` and the appended-event assertions on a command scenario |
26
+ | `Cratis.Arc.Testing` | `22.75.0` | `CommandScenario<TCommand>` and the `CommandResult` assertions |
27
+ | `Cratis.Arc.Chronicle.Testing` | `22.75.0` | `Given`, `EventLog`, `AppendedEvents` and the appended-event assertions on a command scenario |
28
28
  | `Cratis.Chronicle.Testing` | `18.3.0` | `EventScenario`, `ReadModelScenario<T>`, `ReactorScenario<T>` |
29
29
  | `Cratis.Specifications.XUnit` | `4.x` | The `Specification` base and the `ShouldXxx` assertions |
30
30
 
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.
31
+ > Arc command/query/decision scenario surface rechecked at v22.75.0. Arc depends
32
+ > on Chronicle 19.39.1; later Chronicle reactor setup below has its own minimum
33
+ > version and is not downgraded by this Arc verification.
32
34
 
33
35
  Reactor read-model setup and the `EventContext.From` UTC default below are
34
36
  verified separately at **Chronicle 19.46.0**, the minimum version for that
@@ -112,6 +114,7 @@ keep a scenario for that wiring and for appended-event behavior.
112
114
  | Pure command decision, reducer logic or reactor decision | Plain method call with `Specification` (default) | `cratis-specifications-csharp` |
113
115
  | State Change — command pipeline and appended events | `CommandScenario<TCommand>` | this one |
114
116
  | Append semantics, a constraint | `EventScenario` | `cratis-chronicle-event-specifications` |
117
+ | Query pipeline — arguments, authorization, validation, interceptors | `QueryScenario<TReadModel>` | [query and decision scenarios](references/query-and-decision-scenarios.md) |
115
118
  | State View — a projection or reducer | `ReadModelScenario<TReadModel>` | `cratis-chronicle-read-model-specifications` |
116
119
  | Automation or Translation — a reactor | `ReactorScenario<TReactor>` | `cratis-specifications-csharp` |
117
120
  | A host, transport, or real-infrastructure boundary | Out-of-process integration | `cratis-specifications-csharp` |
@@ -203,6 +206,13 @@ produces. Pin an instance only when the events would be noise.
203
206
 
204
207
  Both builder methods return `void`; there is nothing to await.
205
208
 
209
+ That is **legacy** read-model mode: seeded events populate an in-memory model,
210
+ not the append log. For protected or stream decisions call `UseDecisionReads()`
211
+ before seeding; actual events and decisions then share one in-process log.
212
+ Pinned models cannot supply decision guards. Read
213
+ [query and decision scenarios](references/query-and-decision-scenarios.md) when
214
+ specifying query pipelines, routed conflicts, projection admission or completion.
215
+
206
216
  Validator and `Provide()` dependencies are registered in `_scenario.Services`;
207
217
  the concrete validator itself is discovered automatically.
208
218
 
@@ -296,11 +306,11 @@ ideas recorded in `references/provenance.md`. Read `references/provenance.md` wh
296
306
  - **The command scenario reports success but nothing was appended.** The
297
307
  assertion is on `CommandResult` only; add a
298
308
  `ShouldHaveAppendedEvent<TCommand, TEvent>` fact.
299
- - **Injected-validator branches flip with test ordering.** The command-scenario
300
- pipeline can cache enough state that a validator branch depends on execution
301
- order under parallel xUnit runs. Use `CommandScenario` for the valid path, test
302
- rejected state variants by instantiating the validator directly, and only then
303
- put that command's specifications in a small `[Collection]`.
309
+ - **Injected-validator branches flip with test ordering.** This was observed in
310
+ the 22.16-era harness. Arc 22.23.2 stopped replacing the process-wide provider;
311
+ later releases isolated stores and event-source services per scenario. Reproduce
312
+ at 22.75.0 before applying the old direct-validator/small `[Collection]`
313
+ workaround; no current reproduction was verified here.
304
314
 
305
315
  ## How it is proven
306
316
 
@@ -0,0 +1,65 @@
1
+ # Query and decision scenarios
2
+
3
+ <!-- Copyright (c) Cratis. All rights reserved. -->
4
+ <!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
5
+
6
+ Verified at **Arc v22.75.0**. Use the plain `Specification` base for pure decisions;
7
+ these helpers add proof of framework composition, not HTTP transport.
8
+
9
+ ## Query pipeline
10
+
11
+ `QueryScenario<TReadModel>` is in `Cratis.Arc.Testing.Queries` (`Cratis.Arc.Testing`).
12
+ Configure `Services` before the first call. `Perform(nameof(Model.Method),
13
+ arguments?, paging?, sorting?, cancellationToken?)` returns `QueryResult` through
14
+ conversion, validation, authorization and rendering. Use `QueryArguments` from
15
+ `Cratis.Arc.Queries`; there is no principal parameter. Register the principal
16
+ under test with `ICurrentPrincipalAccessor` in `Services`. Scheme-selected queries
17
+ fail closed in-process: HTTP authentication needs a hosted integration spec.
18
+
19
+ At Arc 22.75.1+ (re-verified at 22.76.0), both query and command scenarios preserve
20
+ explicit `ICurrentPrincipalAccessor`, `ICurrentPrincipalOverride` and
21
+ `IAuthorizationPolicyRuntime` registrations rather than replacing them with defaults.
22
+ Configure them before first execution and specify both authorized and denied callers.
23
+ Source: `Source/DotNET/Testing/ScenarioServices.cs`, `Queries/QueryScenario.cs`,
24
+ `Commands/CommandScenario.cs`.
25
+
26
+ `Observe(...)` returns a disposable `QueryObservation`; `Initial` includes the
27
+ first result or rejection and `Next(timeout?, cancellationToken?)` reads the next
28
+ buffered emission (not necessarily distinct). Default deadlines are five seconds;
29
+ timeouts throw. Dispose asynchronously to await cleanup. Interceptors and guards
30
+ run when emissions are consumed. MongoDB `.Observe()` still needs a real DB with
31
+ change streams; the harness does not emulate it.
32
+
33
+ The discovered `IQueryScenarioExtender` supports additional context/services.
34
+ With `Cratis.Arc.Chronicle.Testing` and its Queries namespace, use
35
+ `Given.ForEventSource(id).ReadModel(instance)` or `.Events(...)` before performing.
36
+ The query must read that source via injected `IReadModels`; seeding does not
37
+ replace arbitrary stores or produce later observable emissions after appends.
38
+
39
+ ## Guarded decisions
40
+
41
+ Call `new CommandScenario<TCommand>().UseDecisionReads()` before seeding. This
42
+ mode shares **one real in-process event log** among given events, guarded reads,
43
+ constraints and returned appends. Seed actual events, not pinned read models.
44
+ `Given.ForEventSource(id).OnRoute(route).Events(...)` preserves routing;
45
+ `scenario.EventRoutes.For<TSource>(stream).WithStreamId(...)` creates the route.
46
+ Queue interference using `AppendConcurrently(id, route, event)` and assert a
47
+ nonempty decision is rejected on the intended boundary. An empty identical-retry
48
+ append deliberately returns no scope and cannot prove the conflict guard.
49
+
50
+ `ShouldHaveCompletedStream(type, id)` proves successful completion.
51
+ `DecisionReadAdmissions.ShouldAdmitDecisionReadsIn(assembly)` checks projection
52
+ admission in assembly-wide specs. These APIs are in
53
+ `Cratis.Arc.Chronicle.Testing.Commands`. Routed seeding is refused in legacy mode.
54
+
55
+ The old warnings that command `Given` never reaches the event log and that its
56
+ read-model dictionary is the only backing are **legacy-mode** warnings, not a
57
+ reason to fake protected decision readers. Keep non-key enumeration/provider
58
+ boundaries explicit in either mode; decision mode is not a MongoDB substitute.
59
+
60
+ ## Sources
61
+
62
+ - [Query scenarios](https://github.com/Cratis/Arc/blob/v22.75.0/Documentation/backend/csharp/testing/query-scenario.md)
63
+ - [Stream decisions and routed specs](https://github.com/Cratis/Arc/blob/v22.75.0/Documentation/backend/csharp/chronicle/commands/stream-decisions.md)
64
+ - [Decision-mode extension](https://github.com/Cratis/Arc/blob/v22.75.0/Source/DotNET/Chronicle.Testing/Commands/CommandScenarioChronicleExtensions.cs)
65
+ - [Admission assertion](https://github.com/Cratis/Arc/blob/v22.75.0/Source/DotNET/Chronicle.Testing/Commands/DecisionReadAdmissions.cs)
@@ -15,12 +15,14 @@ told about them. Each has its own extension point.
15
15
 
16
16
  | Package | Version | Purpose |
17
17
  | --- | --- | --- |
18
- | `Cratis.Arc.Core` | `22.41.1` | `Cratis.Arc.Identity`, `Cratis.Arc.Authorization`, `Cratis.Arc.Authentication`, `Cratis.Arc.Tenancy` |
19
- | `Cratis.Arc` | `22.41.1` | ASP.NET Core hosting, `AddMicrosoftIdentityPlatformIdentityAuthentication` |
20
- | `@cratis/arc` | `22.41.1` | `IdentityProvider`, `IIdentity` |
21
- | `@cratis/arc.react` | `22.41.1` | `IdentityProvider` component, `useIdentity`, `RequireRole` |
18
+ | `Cratis.Arc.Core` | `22.75.0` | `Cratis.Arc.Identity`, `Cratis.Arc.Authorization`, `Cratis.Arc.Authentication`, `Cratis.Arc.Tenancy` |
19
+ | `Cratis.Arc` | `22.75.0` | ASP.NET Core hosting, `AddMicrosoftIdentityPlatformIdentityAuthentication` |
20
+ | `@cratis/arc` | `22.75.0` | `IdentityProvider`, `IIdentity` |
21
+ | `@cratis/arc.react` | `22.75.0` | `IdentityProvider` component, `useIdentity`, `RequireRole` |
22
+ | Cratis AuthProxy (`cratis/authproxy`) | `v2.30.5` | the trusted ingress described in [AuthProxy](references/authproxy.md) |
22
23
 
23
- > Re-verified at `22.41.1` (the latest release when written) against that tag's source and Arc's documentation: the types, attributes and members this skill names exist, and the authorization, introspection, identity and emission-guard behavior it describes matches them. **Authorization behavior changed materially after `22.16.0`**, the version this skill used to pin: named policies, schemes, fallback baselines, guest policies and Microsoft-attribute enforcement all arrived later. [authorization](references/authorization.md) has a version table; check it if you run an older version.
24
+ > Rechecked at Arc v22.75.0 against the source-pinned authentication, identity,
25
+ > discovery and authorization references below. **Authorization behavior changed materially after `22.16.0`**, the version this skill used to pin: named policies, schemes, fallback baselines, guest policies and Microsoft-attribute enforcement all arrived later. [authorization](references/authorization.md) has a version table; check it if you run an older version.
24
26
 
25
27
  Reverify before claiming support for another version.
26
28
 
@@ -43,14 +45,17 @@ is the trust boundary. But it means:
43
45
  the application itself to validate a token, that is ordinary ASP.NET Core
44
46
  authentication you configure yourself.
45
47
 
46
- At `22.41.1` the default is that a plain `builder.AddCratis()` host registers the
47
- unsigned-header scheme as its **default** authentication scheme, so any caller who
48
- can reach it can forge a principal for every authorized command and query (open
49
- Arc#2946). Run it only behind that ingress; otherwise use `AddCratisArc()` and
50
- configure a credential-validating scheme. Arc itself refuses the unsigned headers
51
- in one place — protected introspection — until
52
- `Cratis:Arc:Introspection:TrustForwardedIdentityHeaders` is set; see
53
- [authentication](references/authentication.md).
48
+ At **Arc v22.75.0**, forwarded identity headers are ignored unless the host opts
49
+ in with `Cratis:Arc:TrustForwardedIdentityHeaders=true` or
50
+ `builder.AddCratis(options => options.TrustForwardedIdentityHeaders = true)`.
51
+ Header-only requests otherwise remain anonymous. `AddCratis()` still registers
52
+ the header scheme; it does not make the headers trusted. Opt in only behind the
53
+ ingress above, otherwise configure a credential-validating scheme. The old
54
+ `Introspection:TrustForwardedIdentityHeaders` setting is obsolete and enables the
55
+ same host-wide flag. See [authentication](references/authentication.md).
56
+ Source: [Arc v22.75.0 secure defaults](https://github.com/Cratis/Arc/blob/v22.75.0/Documentation/upgrading/secure-defaults.mdx).
57
+
58
+ Cratis AuthProxy is the ingress that meets these obligations for Cratis applications. Read [AuthProxy](references/authproxy.md) when the application is deployed behind it (header contract, `/.cratis/me` fail-closed verification, tenant headers).
54
59
 
55
60
  ## Identity details — what the frontend is told
56
61
 
@@ -85,45 +90,36 @@ public class <Name>IdentityDetailsProvider : IProvideIdentityDetails<<Details>>
85
90
 
86
91
  | Route | What it does |
87
92
  | --- | --- |
88
- | `GET /.cratis/me` | The current identity. 401 when not authenticated, 403 when the provider says not authorized, otherwise the result plus the identity cookie |
93
+ | `GET /.cratis/me` | The current identity. 401 when not authenticated, 403 when the provider says not authorized, otherwise the principal-derived identity JSON |
89
94
  | `GET /.cratis/identity-details/schema` | The JSON schema of the details type (`{}` without a details provider) |
90
95
  | `GET /.cratis/users` | Development tooling — aggregates every `ICanProvideUsers` |
91
96
  | `GET /.cratis/tenants` | Development tooling — aggregates every `ICanProvideTenants` |
92
97
  | `GET /.cratis/commands`, `GET /.cratis/queries` | Introspection catalogs of operation names, types and routes (`MapIntrospectionEndpoints()`) |
93
98
 
94
- Normal `UseCratisArc()` activation maps all of these `AllowAnonymous` by default,
95
- **including in Production**, unless an endpoint of the same name already exists; `/.cratis/me`
96
- does its own 401/403. There is no public constant for the identity routes — they
97
- are string literals in the endpoint mapper.
98
-
99
- ⚠️ **A fallback policy does not protect an endpoint that is explicitly anonymous**,
100
- and the word "development" is not an environment check. At `22.41.1` the default is that
101
- these routes are public and expose your command, query, user and tenant surface;
102
- the exposure is tracked in open Arc#2834. To change it:
103
-
104
- - the two catalogs: configure `Cratis:Arc:Introspection` (`Enabled: false` unmaps
105
- them; `RequireAuthentication`/`Roles` protect them; the
106
- `TrustForwardedIdentityHeaders` opt-in is for the header handler only behind a
107
- trusted ingress) — options table in [authentication](references/authentication.md);
108
- - `/.cratis/identity-details/schema`, `/.cratis/users` and `/.cratis/tenants` are
109
- **not** affected by those options. Return fixtures only from the user and tenant
110
- providers, and restrict all three at the trusted ingress.
111
-
112
- Test the discovery endpoints under Production settings, with your fallback policy
113
- enabled.
114
-
115
- `/.cratis/me` writes the result as JSON **and** as a base64 cookie named
116
- `.cratis-identity`, deliberately **not `HttpOnly`** so the frontend can read it
117
- without a round trip. `Secure` follows whether the request is HTTPS, `SameSite`
118
- is `Lax`, `Path` is `/`.
119
-
120
- ⚠️ A non-`HttpOnly` cookie the browser can read is a cookie the user can edit.
121
- Treat everything in it as a **display** input. Never let it decide anything the
122
- server has not already decided. At `22.41.1` the default is that `/.cratis/me` returns
123
- whatever a presented `.cratis-identity` cookie decodes to *before* it looks at the
124
- authenticated principal, so a client can make it report any identity or roles
125
- (open Arc#2939). Commands and queries authorize on the request principal and are
126
- not affected; a frontend that shows or branches on `/.cratis/me` can be misled.
99
+ At **v22.75.0**, discovery is anonymous only when the host's
100
+ `IHostEnvironment.IsDevelopment()` is true. Elsewhere it requires authentication;
101
+ if the host cannot authenticate callers, the routes are not mapped (404) and a
102
+ warning is logged. `RequireAuthentication` unset selects that environment default;
103
+ explicit `true` requires authentication everywhere (startup fails if unavailable);
104
+ explicit `false` exposes discovery anonymously everywhere, warning outside
105
+ Development. `Roles` implies authentication and cannot combine with explicit
106
+ `false`. `Enabled=false` removes the two catalogs; `IdentityDiscovery=false`
107
+ removes users, tenants and the details schema. Setting both removes all discovery
108
+ without authentication checks; `/.cratis/me` remains. `[ExcludeFromDiscovery]`
109
+ hides commands, read models, queries and controllers from catalogs/OpenAPI, **not
110
+ from invocation or authorization**. These are .NET settings, not a JVM contract.
111
+ Source: [secure defaults](https://github.com/Cratis/Arc/blob/v22.75.0/Documentation/upgrading/secure-defaults.mdx).
112
+
113
+ `/.cratis/me` derives identity only from the authenticated request principal,
114
+ never from `.cratis-identity`. The details provider runs for each authenticated
115
+ identity request; `ModifyDetails` changes only the current response. Arc no
116
+ longer writes the readable identity cookie (it expires a legacy one). Identity
117
+ responses carry `Cache-Control: no-store, private` and `Vary: Cookie`.
118
+ Stop reading the cookie; use `IdentityProvider.getCurrent()`/`refresh()` and
119
+ `clearCache()` on logout. The frontend caches in memory, with a warned legacy
120
+ cookie fallback **only after a 404**, never after 401/403.
121
+ Sources: [IdentityProvider.cs](https://github.com/Cratis/Arc/blob/v22.75.0/Source/DotNET/Arc.Core/Identity/IdentityProvider.cs),
122
+ [IdentityProvider.ts](https://github.com/Cratis/Arc/blob/v22.75.0/Source/JavaScript/Arc/identity/IdentityProvider.ts).
127
123
 
128
124
  ## Authorization — what Arc evaluates
129
125
 
@@ -144,10 +140,10 @@ and `AuthenticationSchemes` enforce nothing, and Microsoft's `[Authorize]` is **
144
140
  honored on model-bound commands and read models, so a command protected only by it
145
141
  is open. From `22.21.0` Microsoft's `[Authorize]`/`[AllowAnonymous]` are enforced on
146
142
  the ASP.NET Core host (never on the standalone Core host), and from `22.23.0` named
147
- policies and schemes are evaluated. The rest of this section is `22.41.1` behavior;
143
+ policies and schemes are evaluated. The rest of this section is `22.75.0` behavior;
148
144
  see [authorization](references/authorization.md) for the version table.
149
145
 
150
- At `22.41.1`:
146
+ At `22.75.0`:
151
147
 
152
148
  - **Named policies work.** `[Authorize(Policy = "…")]` requires the policy to
153
149
  succeed asynchronously. Implement
@@ -204,7 +200,8 @@ restrictive verdict wins, and a guard that throws, or cannot be constructed, fai
204
200
  closed — the latter denies every observable query in the process. The principal comes
205
201
  from `ObservableQueryEmissionContext.Principal`, never from ambient accessors or
206
202
  `IHttpContextAccessor`; the guard is constructed per emission, so keep it cheap and
207
- cache in a singleton. Guards do not protect the HTTP snapshot path. Read
203
+ cache in a singleton. Since 22.69.1, guards and read-model interceptors also
204
+ protect plain HTTP snapshots, waits and long polls. Read
208
205
  [observable emission guards](references/observable-emission-guards.md) when implementing or reviewing revocation of an observable-query subscription.
209
206
 
210
207
  ### Validators are not an authorization boundary
@@ -281,7 +278,7 @@ no details to give, not only when the identity is explicitly unset.
281
278
  `whileLoading` and `forbidden` slots.
282
279
 
283
280
  ⚠️ **`RequireRole` hides UI; it does not protect data.** The identity it reads
284
- comes from the deliberately non-`HttpOnly` cookie above. Every rule it expresses
281
+ comes from the principal-derived identity endpoint, but browser state is editable. Every rule it expresses
285
282
  must also exist on the server as `[Authorize]`/`[Roles]` or an authorization
286
283
  filter.
287
284
 
@@ -300,11 +297,10 @@ implements as `ICanProvideUsers` and `ICanProvideTenants`.
300
297
  To run locally with an identity, supply the forwarded headers yourself — see
301
298
  [local development](references/local-development.md).
302
299
 
303
- ⚠️ Those two endpoints are `AllowAnonymous` and mapped unconditionally, in
304
- Production too, and a fallback policy does not protect them. If your
305
- `ICanProvideUsers` implementation returns real users, that list is public. Return
306
- development fixtures only, and gate the implementation on a development
307
- environment check.
300
+ Those endpoints follow the discovery exposure settings above. Return development
301
+ fixtures from development providers; test deployed discovery access explicitly.
302
+ Forwarded headers sent locally require the host-wide trust opt-in too; do not
303
+ carry that shortcut into a directly reachable deployed host.
308
304
 
309
305
  ## Verify
310
306
 
@@ -330,7 +326,7 @@ environment check.
330
326
  - Discovery endpoints (`/.cratis/commands`, `/.cratis/queries`,
331
327
  `/.cratis/identity-details/schema`, `/.cratis/users`, `/.cratis/tenants`) are
332
328
  tested under Production settings; introspection is restricted or disabled where it
333
- must not be public, and the rest are restricted at the ingress.
329
+ must not be public, including identity discovery.
334
330
  - `TrustForwardedIdentityHeaders` is set only behind an ingress that authenticates
335
331
  callers, strips caller-supplied identity headers, writes trusted ones and blocks
336
332
  direct backend access; custom handlers honor it.