@cratis/pi 2.22.0 → 2.23.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 (111) hide show
  1. package/package/corpus/agents/backend-developer.md +6 -5
  2. package/package/corpus/agents/code-reviewer.md +7 -7
  3. package/package/corpus/agents/coordinator.md +5 -4
  4. package/package/corpus/agents/frontend-developer.md +34 -40
  5. package/package/corpus/agents/orchestrator.md +5 -4
  6. package/package/corpus/agents/performance-reviewer.md +5 -4
  7. package/package/corpus/agents/planner.md +5 -4
  8. package/package/corpus/agents/repository-investigation-reviewer.md +2 -1
  9. package/package/corpus/agents/repository-investigator.md +2 -1
  10. package/package/corpus/agents/security-reviewer.md +5 -4
  11. package/package/corpus/agents/slice-implementer.md +8 -2
  12. package/package/corpus/agents/spec-writer.md +6 -4
  13. package/package/corpus/harnesses/pi/extensions/cratis-rules/index.ts +233 -10
  14. package/package/corpus/harnesses/pi/extensions/subagent/agents.ts +52 -7
  15. package/package/corpus/harnesses/pi/extensions/subagent/index.ts +14 -0
  16. package/package/corpus/hooks/README.md +29 -27
  17. package/package/corpus/hooks/agent-stop.md +1 -5
  18. package/package/corpus/hooks/scripts/cratis-patterns.json +1 -1
  19. package/package/corpus/hooks/scripts/validate-package-subpaths.sh +1 -2
  20. package/package/corpus/prompts/ship-changes.prompt.md +4 -0
  21. package/package/corpus/prompts/verify-ai-setup.prompt.md +17 -7
  22. package/package/corpus/rules/ai-distribution.md +60 -0
  23. package/package/corpus/rules/application-profile.md +73 -0
  24. package/package/corpus/rules/components.md +77 -44
  25. package/package/corpus/rules/dialogs.md +24 -20
  26. package/package/corpus/rules/efcore.md +35 -187
  27. package/package/corpus/rules/framework.md +1 -1
  28. package/package/corpus/rules/frontend-quality.md +1 -1
  29. package/package/corpus/rules/frontend-testing.md +4 -4
  30. package/package/corpus/rules/general.md +21 -162
  31. package/package/corpus/rules/guards-and-fuses.md +24 -10
  32. package/package/corpus/rules/local-work-artifacts.md +6 -0
  33. package/package/corpus/rules/new-repository-intake.md +34 -0
  34. package/package/corpus/rules/orleans.md +13 -27
  35. package/package/corpus/rules/profiles.md +6 -0
  36. package/package/corpus/rules/react.md +12 -12
  37. package/package/corpus/rules/reactors.md +28 -5
  38. package/package/corpus/rules/rtk.md +13 -3
  39. package/package/corpus/rules/specs.scenarios.csharp.md +18 -3
  40. package/package/corpus/rules/specs.typescript.md +1 -1
  41. package/package/corpus/rules/storybook.md +2 -2
  42. package/package/corpus/rules/typescript.md +1 -0
  43. package/package/corpus/rules/verification-discipline.md +7 -1
  44. package/package/corpus/rules/vertical-slices.md +20 -15
  45. package/package/corpus/rules/writing-correct-examples.md +1 -0
  46. package/package/corpus/skills/cratis-application-react-specifications/SKILL.md +4 -4
  47. package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +16 -8
  48. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +6 -4
  49. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +1 -1
  50. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +1 -1
  51. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +1 -1
  52. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +1 -1
  53. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +7 -4
  54. package/package/corpus/skills/cratis-arc-command/SKILL.md +9 -7
  55. package/package/corpus/skills/cratis-arc-command/references/command-result.md +1 -1
  56. package/package/corpus/skills/cratis-arc-command/references/handler-shapes.md +2 -2
  57. package/package/corpus/skills/cratis-arc-command/references/proxy-generation.md +6 -3
  58. package/package/corpus/skills/cratis-arc-command/references/read-model-injection.md +1 -1
  59. package/package/corpus/skills/cratis-arc-command-execution/SKILL.md +12 -2
  60. package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +5 -3
  61. package/package/corpus/skills/cratis-arc-ef-core-migration/SKILL.md +7 -4
  62. package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +10 -3
  63. package/package/corpus/skills/cratis-arc-query-paging/SKILL.md +13 -8
  64. package/package/corpus/skills/cratis-arc-react-page/SKILL.md +27 -24
  65. package/package/corpus/skills/cratis-arc-react-page/references/data-page.md +15 -11
  66. package/package/corpus/skills/cratis-arc-react-page/references/data-tables.md +9 -2
  67. package/package/corpus/skills/cratis-arc-react-page/references/dialogs.md +11 -9
  68. package/package/corpus/skills/cratis-arc-react-page/references/queries-and-commands.md +26 -2
  69. package/package/corpus/skills/cratis-chronicle-cli-operations/SKILL.md +19 -13
  70. package/package/corpus/skills/cratis-chronicle-client-dotnet/SKILL.md +17 -15
  71. package/package/corpus/skills/cratis-chronicle-client-elixir/SKILL.md +24 -20
  72. package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +26 -15
  73. package/package/corpus/skills/cratis-chronicle-client-typescript/SKILL.md +15 -11
  74. package/package/corpus/skills/cratis-chronicle-compliance/SKILL.md +14 -3
  75. package/package/corpus/skills/cratis-chronicle-event-constraints/SKILL.md +13 -6
  76. package/package/corpus/skills/cratis-chronicle-event-modeling/SKILL.md +2 -2
  77. package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +11 -5
  78. package/package/corpus/skills/cratis-chronicle-event-type-migration/SKILL.md +2 -2
  79. package/package/corpus/skills/cratis-chronicle-multi-tenancy/SKILL.md +2 -2
  80. package/package/corpus/skills/cratis-chronicle-projection/SKILL.md +23 -4
  81. package/package/corpus/skills/cratis-chronicle-projection/references/fluent-builder.md +1 -1
  82. package/package/corpus/skills/cratis-chronicle-projection/references/model-bound-attributes.md +2 -2
  83. package/package/corpus/skills/cratis-chronicle-reactor/SKILL.md +22 -8
  84. package/package/corpus/skills/cratis-chronicle-read-model/SKILL.md +1 -1
  85. package/package/corpus/skills/cratis-chronicle-read-model/references/queries.md +1 -1
  86. package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +3 -1
  87. package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +1 -1
  88. package/package/corpus/skills/cratis-chronicle-web-workbench/SKILL.md +19 -11
  89. package/package/corpus/skills/cratis-cli-terminal-workbench/SKILL.md +11 -6
  90. package/package/corpus/skills/cratis-code-review/SKILL.md +8 -6
  91. package/package/corpus/skills/cratis-components-accessibility/SKILL.md +112 -82
  92. package/package/corpus/skills/cratis-components-schema-editor/SKILL.md +12 -10
  93. package/package/corpus/skills/cratis-components-stepper-command-dialog/SKILL.md +11 -12
  94. package/package/corpus/skills/cratis-components-styling/SKILL.md +204 -170
  95. package/package/corpus/skills/cratis-components-toolbar/SKILL.md +32 -26
  96. package/package/corpus/skills/cratis-event-model-diagram/SKILL.md +1 -1
  97. package/package/corpus/skills/cratis-fundamentals-concept/SKILL.md +2 -2
  98. package/package/corpus/skills/cratis-fundamentals-type-discovery/SKILL.md +2 -2
  99. package/package/corpus/skills/cratis-lens-browser-extension/SKILL.md +4 -2
  100. package/package/corpus/skills/cratis-performance-review/SKILL.md +4 -2
  101. package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +15 -5
  102. package/package/corpus/skills/cratis-security-review/SKILL.md +20 -3
  103. package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +3 -3
  104. package/package/corpus/skills/cratis-specifications-csharp/references/application-scenarios.md +12 -8
  105. package/package/corpus/skills/cratis-specifications-csharp/references/csharp-patterns.md +1 -1
  106. package/package/corpus/skills/cratis-specifications-csharp/references/integration-specs.md +7 -3
  107. package/package/corpus/skills/cratis-specifications-typescript/SKILL.md +6 -3
  108. package/package/corpus/skills/cratis-specifications-typescript/references/typescript-patterns.md +21 -4
  109. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +29 -17
  110. package/package.json +1 -1
  111. package/package/corpus/rules/terminal-commands.md +0 -19
@@ -12,33 +12,37 @@ to yours, and everything after that is ordinary OTP.
12
12
 
13
13
  ## Verified product sources
14
14
 
15
- This skill is verified against `Cratis/Chronicle.Elixir` at tag **`v2.2.0`**,
16
- which is the version actually published on Hex.
15
+ This skill was written against `Cratis/Chronicle.Elixir` at tag **`v2.2.0`** and
16
+ re-verified at **`v3.1.0`** (published on Hex as `3.1.0`; `3.0.0` is the
17
+ Chronicle 17/18 wire migration — gRPC request/response shapes, no public API
18
+ removed). Every module, function and option this skill names exists at `v3.1.0`;
19
+ the `file:line` citations below were taken at `v2.2.0` and may have shifted.
17
20
 
18
21
  | Fact | Value | Source |
19
22
  | --- | --- | --- |
20
23
  | OTP app / Hex package | `:cratis_chronicle` | `Source/chronicle/mix.exs:14` |
21
- | Published version | `2.2.0` | hex.pm |
22
- | Elixir requirement | `~> 1.14` | `Source/chronicle/mix.exs:16` |
23
- | Contracts dependency | `cratis_chronicle_contracts` resolved to `16.13.4` | `Source/chronicle/mix.lock` |
24
+ | Published versions | `3.1.0`, `3.0.0`, `2.2.0`, `2.1.x`, `2.0.0`, `1.0.x`, `0.x` — **no `2.3.0`** | hex.pm |
25
+ | Elixir requirement | `~> 1.14` | `Source/chronicle/mix.exs` |
26
+ | Contracts dependency | `cratis_chronicle_contracts` resolved to `18.2.0` at `v3.1.0` (`16.13.4` at `v2.2.0`) | `Source/chronicle/mix.lock` |
24
27
 
25
28
  > **The `v2.3.0` git tag was never published to Hex.** `mix deps.get` cannot
26
- > fetch it. Anything introduced there — notably the `Chronicle.Concept` macro,
27
- > which does not exist at `v2.2.0` — must not be documented as available.
29
+ > fetch it. What it introduced — notably the `Chronicle.Concept` macro
30
+ > (`use Chronicle.Concept, type: :string`, a single-value concept that carries its
31
+ > `pii` classification once, mirroring `ConceptAs<T>`) — **is published from
32
+ > `3.0.0`**. On `2.2.0` it does not exist; on `3.x` it is the preferred way to
33
+ > classify a personal value.
28
34
 
29
35
  > **The repository's `VERSION` file says `0.0.5` and is a build-time
30
36
  > placeholder** overwritten by the publish workflow. Never quote it.
31
37
 
32
- > **Do not copy from the repository's root `README.md`.** At `v2.2.0` it uses
33
- > module names that do not exist — `use Chronicle.EventType`,
34
- > `use Chronicle.ReadModel`, `use Chronicle.Reactor`, `use Chronicle.Reducer`,
35
- > `use Chronicle.Seeder` (`README.md:9-13`, `:49`, `:54`). The real modules are
36
- > namespaced: `Chronicle.Events.EventType`, `Chronicle.ReadModels.ReadModel`,
37
- > `Chronicle.Reactors.Reactor`, `Chronicle.Reducers.Reducer`,
38
- > `Chronicle.Seeding.Seeder`. **Copy from `Documentation/client-snippets/`
39
- > instead** — those are CI-validated and correct at every version.
38
+ > **The real modules are namespaced** — `Chronicle.Events.EventType`,
39
+ > `Chronicle.ReadModels.ReadModel`, `Chronicle.Reactors.Reactor`,
40
+ > `Chronicle.Reducers.Reducer`, `Chronicle.Seeding.Seeder`. The `v2.2.0` root
41
+ > `README.md` used unnamespaced names that do not exist (`use Chronicle.EventType`,
42
+ > …); the `3.x` README is correct. When in doubt, copy from
43
+ > `Documentation/client-snippets/` — those are CI-validated at every version.
40
44
 
41
- > `Documentation/get-started.md:12` pins `{:cratis_chronicle, "~> 0.1"}`, which
45
+ > `Documentation/get-started.md:13` pins `{:cratis_chronicle, "~> 0.1"}`, which
42
46
  > does not match the published `2.2.0`. Use a `2.x` requirement.
43
47
 
44
48
  ## Adding it
@@ -211,7 +215,7 @@ the outcome.
211
215
  | `get_from_sequence_number/2` | | `:369` |
212
216
  | `get_tail_sequence_number/2` | | `:408` |
213
217
 
214
- Append options (`event_log.ex:79-94`): `:client`, `:namespace`,
218
+ Append options (`event_log.ex:79-94`, plus `:occurred` read at `:720`): `:client`, `:namespace`,
215
219
  `:event_sequence_id` (default `"event-log"`), `:event_source_type` (default
216
220
  `"Default"`), `:event_stream_type` (default **`"All"`**), `:event_stream_id`
217
221
  (default `"Default"`), `:tags`, `:subject`, `:correlation_id`, `:identity`,
@@ -398,9 +402,9 @@ attempt.
398
402
 
399
403
  | Pitfall | Why it bites |
400
404
  | --- | --- |
401
- | Copying the root `README.md` | Its module names do not exist at `v2.2.0` |
402
- | Using `Chronicle.Concept` | It exists only in the unpublished `v2.3.0` tag |
403
- | `{:cratis_chronicle, "~> 0.1"}` from the docs | Does not match the published `2.2.0` |
405
+ | Copying the `v2.2.0` root `README.md` | Its module names do not exist; the `3.x` README is correct |
406
+ | Using `Chronicle.Concept` on `2.2.0` | It ships from `3.0.0`; on `2.2.0` annotate fields with `pii/1,2` instead |
407
+ | Copying the dependency line from the README | It lags the published version; take the version from hex.pm |
404
408
  | Quoting the `VERSION` file | It is a `0.0.5` build-time placeholder |
405
409
  | Putting configuration in `config.exs` | The client reads no application env; options are child-spec keywords |
406
410
  | Omitting `:otp_app` | Discovery falls back to scanning every loaded module |
@@ -13,14 +13,20 @@ you construct a client, ask it for an event store, and append or observe.
13
13
 
14
14
  ## Verified product sources
15
15
 
16
- This skill is verified against this exact source:
16
+ This skill was written against `Chronicle.Kotlin` at tag `v4.0.0` (commit
17
+ `63ff629`) and re-verified at **`v5.1.0`**: every class, function, option and
18
+ Spring bean this skill names exists there. The `4→5` major is the Chronicle 17/18
19
+ wire migration (gRPC request/response shapes and `ensureSuccess` result
20
+ envelopes — internal to the client); `5.0.0` also adds a `ChronicleCommandRejected`
21
+ exception for a rejected server-side command. The `file:line` citations below
22
+ were taken at `v4.0.0` and may have shifted.
17
23
 
18
24
  | Artifact | Version | Verified from |
19
25
  | --- | --- | --- |
20
- | `io.cratis:chronicle` | `4.0.0` | `Chronicle.Kotlin` at tag `v4.0.0` (commit `63ff629`) |
21
- | `io.cratis:chronicle-spring-boot-starter` | `4.0.0` | same tag, `Integrations/SpringBoot` |
22
- | `io.cratis:chronicle-testing` | `4.0.0` | same tag, `Testing` |
23
- | `io.cratis:chronicle-contracts` | `16.44.1` | `Source/build.gradle.kts:11` |
26
+ | `io.cratis:chronicle` | `5.1.0` | `Chronicle.Kotlin` at tag `v5.1.0` |
27
+ | `io.cratis:chronicle-spring-boot-starter` | `5.1.0` | same tag, `Integrations/SpringBoot` |
28
+ | `io.cratis:chronicle-testing` | `5.1.0` | same tag, `Testing` |
29
+ | `io.cratis:chronicle-contracts` | `18.2.0` | `Source/build.gradle.kts` (`chronicleContractsVersion`) |
24
30
 
25
31
  All three artifacts are published to Maven Central under `io.cratis`
26
32
  (`Source/build.gradle.kts:54`, `Testing/build.gradle.kts:32`,
@@ -35,9 +41,10 @@ All three artifacts are published to Maven Central under `io.cratis`
35
41
  > the checked-in source never carries the real number. Take it from Maven
36
42
  > Central.
37
43
 
38
- > **The client and the kernel version independently.** Client `4.0.0` is built
39
- > against `chronicle-contracts` `16.44.1`; the Chronicle server has since moved
40
- > to a `17.x` line. Confirm the client/server pair you intend to run is
44
+ > **The client and the kernel version independently.** Client `5.1.0` is built
45
+ > against `chronicle-contracts` `18.2.0` (the `4.x` line was built against
46
+ > `16.44.1` and does not speak the 17/18 wire shapes); the Chronicle server is
47
+ > on `18.x`. Confirm the client/server pair you intend to run is
41
48
  > supported before relying on it — do not infer compatibility from the fact that
42
49
  > both are "latest".
43
50
 
@@ -82,11 +89,11 @@ fun main() = runBlocking {
82
89
  | `fun evictEventStores()` | `:36` | drops the local cache, keeps the client |
83
90
  | `fun dispose()` | `:39` | `IChronicleClient : AutoCloseable`, `close()` delegates to it (`:41`) |
84
91
 
85
- **The client connects in its constructor.** `ChronicleClient.kt:13` is
92
+ **The client connects in its constructor.** `ChronicleClient.kt:12` is
86
93
  `ChronicleConnection(options.connectionString).also { it.connect() }` — there is
87
94
  no separate `connect()` step to call, and constructing the client is the
88
95
  connecting act. Event stores are cached per `"$name/$namespace"`
89
- (`ChronicleClient.kt:22`), so repeated `getEventStore` calls return the same
96
+ (`ChronicleClient.kt:21`), so repeated `getEventStore` calls return the same
90
97
  instance.
91
98
 
92
99
  The default namespace is the literal `"Default"` —
@@ -355,13 +362,15 @@ A model-bound projection puts the projection on the read model with `@FromEvent`
355
362
  (`projections/SetFrom.kt:22`); the full family also includes `SetValue`,
356
363
  `SetFromContext`, `AddFrom`, `SubtractFrom`, `Increment`, `Decrement`, `Count`,
357
364
  `Join`, `RemovedWith`, `RemovedWithJoin`, `ChildrenFrom`, `ClearWith`,
358
- `FromAll`, `FromEvery`, `Nested`, `NoAutoMap`, `NotRewindable`. The declarative
365
+ `FromAll`, `FromEvery`, `FromEventSourceId`, `CountFromAll`, `IncrementFromAll`,
366
+ `DecrementFromAll`, `Nested`, `NoAutoMap`, `NoAutoMapProperties`,
367
+ `NotRewindable`. The declarative
359
368
  alternative is a class implementing `IProjectionFor<TReadModel>`.
360
369
 
361
370
  ## Discovery and registration
362
371
 
363
372
  `ClientArtifacts` scans the classpath with ClassGraph
364
- (`artifacts/ClientArtifacts.kt:44-48`), and `ClientArtifacts.default` is a
373
+ (`artifacts/ClientArtifacts.kt:66-98`), and `ClientArtifacts.default` is a
365
374
  process-wide lazy singleton (`:156`). What it looks for (`:66-98`):
366
375
 
367
376
  | Kind | Rule |
@@ -377,6 +386,8 @@ process-wide lazy singleton (`:156`). What it looks for (`:66-98`):
377
386
  | seeders | implements `ICanSeedEvents` |
378
387
  | webhooks | implements `IWebhookDefiner` |
379
388
  | captures | implements `ICapture` |
389
+ | reactor middlewares | implements `IReactorMiddleware` or `BlockingReactorMiddleware` |
390
+ | reactor argument resolvers | implements `IReactorMethodArgumentResolver` or `BlockingReactorMethodArgumentResolver` |
380
391
 
381
392
  > The `FromEvent$Container` entry is not incidental: Kotlin's `@Repeatable`
382
393
  > replaces repeated annotations with a synthetic container, so a class carrying
@@ -464,7 +475,7 @@ In Spring Boot, Java injects the `Chronicle` bean instead — it already takes
464
475
  folds a reducer through the same handler-shape rules as production
465
476
  (`ReadModelScenario.kt:139-150`). Its scope is small — appends and reducer folds
466
477
  only; there is no in-process reactor, projection, or constraint scenario
467
- (`Testing/api/Testing.api` is 56 lines).
478
+ (`Testing/api/Testing.api` is 57 lines).
468
479
 
469
480
  ## Common pitfalls
470
481
 
@@ -474,9 +485,9 @@ only; there is no in-process reactor, projection, or constraint scenario
474
485
  | Two `@EventType` classes sharing a simple name | The id defaults to the simple name, so they collide on the wire (`EventTypesService.kt:67`) |
475
486
  | Shipping the default connection string to production | `skipTlsValidation` defaults to **true**; certificate validation is off (`ChronicleConnectionString.kt:28`) |
476
487
  | Copying a version from the README or docs | Both are stale at `v4.0.0`; the real version comes from the release, not the source |
477
- | Expecting `getEventStore` to suspend | It does not — the client already connected in its constructor (`ChronicleClient.kt:13`) |
488
+ | Expecting `getEventStore` to suspend | It does not — the client already connected in its constructor (`ChronicleClient.kt:12`) |
478
489
  | Expecting `awaitRegistration()` to mean "registered" | It completes in a `finally`, so it also returns after a failed pass (`ArtifactRegistrations.kt:47-57`) |
479
- | Expecting a registration failure to throw | Failures are printed to `System.err`, not raised (`EventStore.kt:236`) |
490
+ | Expecting a registration failure to throw | Failures are printed to `System.err`, not raised (`EventStore.kt:244`) |
480
491
  | Expecting read model reactors to be discovered | `IReadModelReactor` is absent from the scan and from `IEventStore`; construct `ReadModelReactors(...)` yourself |
481
492
  | Leaving the default classpath scan on in a large app | Use `withArtifactsFrom(...)` or `artifact-packages` to narrow it (`ChronicleOptions.kt:58`) |
482
493
  | Expecting WebFlux support from the starter | The web auto-configuration is `SERVLET`-only (`ChronicleWebAutoConfiguration.kt:29`) |
@@ -13,13 +13,17 @@ HTTP and never sees this package.
13
13
 
14
14
  ## Verified product sources
15
15
 
16
- This skill is verified against `Cratis/Chronicle.TypeScript` at tag `v4.0.0`
17
- (commit `bed0b86`), which is the version published on npm.
16
+ This skill was written against `Cratis/Chronicle.TypeScript` at tag `v4.0.0`
17
+ (commit `bed0b86`) and re-verified at **`v5.1.0`** (published on npm as
18
+ `5.1.0`): every class, function and option this skill names exists there. The
19
+ `4→5` major is the Chronicle 17/18 wire migration (internal gRPC shapes; it adds
20
+ `ensureCommandResponse` for server-side command results). The `file:line`
21
+ citations below were taken at `v4.0.0` and may have shifted.
18
22
 
19
23
  | Package | Version | Where |
20
24
  | --- | --- | --- |
21
- | `@cratis/chronicle` | `4.0.0` | `Source/package.json:2` |
22
- | `@cratis/chronicle.contracts` | `17.0.0` | `Source/package.json` dependency |
25
+ | `@cratis/chronicle` | `5.1.0` | npm; `Source/package.json` carries a `1.0.0` placeholder |
26
+ | `@cratis/chronicle.contracts` | `18.2.0` | `Source/package.json` dependency at `v5.1.0` |
23
27
  | `@cratis/fundamentals` | `^7.14.0` | **peer dependency** — you install it |
24
28
 
25
29
  > **Do not read the version out of the repository.** `Source/package.json` says
@@ -75,7 +79,7 @@ try {
75
79
  }
76
80
  ```
77
81
 
78
- `IChronicleClient` is three members — `Source/IChronicleClient.ts:16-39`:
82
+ `IChronicleClient` is four members — `Source/IChronicleClient.ts:16-39`:
79
83
 
80
84
  | Member | Line |
81
85
  | --- | --- |
@@ -125,7 +129,7 @@ Two facts to carry into production guidance:
125
129
  is not validated, because a development kernel serves a self-signed
126
130
  certificate. Set `?skipTlsValidation=false` against a real server.
127
131
  - **A connection string with no credentials silently falls back to the
128
- development client credentials** (`Source/connection/ChronicleConnection.ts`).
132
+ development client credentials** (`Source/connection/ChronicleConnection.ts:319-326`; the constants live in `ChronicleConnectionString.ts:453-454`).
129
133
  An anonymous-looking connection string is not anonymous; it is
130
134
  `chronicle-dev-client`.
131
135
 
@@ -208,13 +212,13 @@ if (!result.isSuccess) {
208
212
  `EventContext.sequenceNumber` is a `bigint` too.
209
213
  - `AppendOptions` — `Source/eventSequences/AppendOptions.ts`: `correlationId?`,
210
214
  `eventSourceId?`, `concurrencyScope?`, `concurrencyScopes?`, and **`tags?`**
211
- (`:22-29`, new in `4.0.0`).
215
+ (`:22-29`, since `4.0.0`; unchanged at `5.1.0`).
212
216
 
213
217
  > **Single `append()` cannot target a stream or a subject.** It hardcodes
214
218
  > `EventSourceType: 'Default'`, `EventStreamType: 'Default'`,
215
219
  > `EventStreamId: eventSourceId`, and `Subject: eventSourceId`
216
- > (`Source/eventSequences/EventSequence.ts:100-119`). Only `tags` became a real
217
- > option in `4.0.0` (`:117`). To target a different stream or subject, use the
220
+ > (`Source/eventSequences/EventSequence.ts:100-120`). Only `tags` became a real
221
+ > option in `4.0.0` (`:118`). To target a different stream or subject, use the
218
222
  > `EventForEventSourceId[]` overload, whose entries carry `eventStreamType`,
219
223
  > `eventStreamId`, `eventSourceType`, `subject`, and `tags`.
220
224
 
@@ -252,7 +256,7 @@ Reducers do the same (`Source/reducers/Reducers.ts:454-460`). A handler whose
252
256
  method name does not match is simply never called — **there is no error**.
253
257
 
254
258
  > The docstring at `Source/reactors/reactor.ts:28-29` claims dispatch is by the
255
- > first parameter's type. **It is wrong**, and it is still wrong at `4.0.0`. The
259
+ > first parameter's type. **It is wrong**, and it is still wrong at `5.1.0`. The
256
260
  > code is the authority; so are the client snippets under `Documentation/`, which
257
261
  > state the name rule explicitly.
258
262
 
@@ -281,7 +285,7 @@ a failed side-effect append fails the partition.
281
285
 
282
286
  `@reducer(id?, eventSequenceId?, readModel?, isActive?)` —
283
287
  `Source/reducers/reducer.ts:58`. Passing `readModel` also registers that type as a
284
- read model. **The reducer handler takes three arguments at `4.0.0`** —
288
+ read model. **The reducer handler takes three arguments (`4.0.0` through `5.1.0`)** —
285
289
  `reducerInstance[methodName](content, currentState, context)`
286
290
  (`Source/reducers/Reducers.ts:400`). The third is optional by arity, and a handler
287
291
  may be sync or async.
@@ -17,8 +17,8 @@ This skill is verified against this exact public release:
17
17
 
18
18
  | Package | Version | Purpose |
19
19
  | --- | --- | --- |
20
- | `Cratis.Chronicle` | `16.45.3` | `Cratis.Chronicle.Compliance.GDPR`, `Cratis.Chronicle.SubjectAttribute`, `IEventSequence.Redact`, `IReadModels.Release` |
21
- | `Cratis.Chronicle.CodeAnalysis` | `16.45.3` | `CHR0026`, `CHR0034`, `CHR0035`, `CHR0043`, `CHR0046` |
20
+ | `Cratis.Chronicle` | `18.3.0` | `Cratis.Chronicle.Compliance.GDPR`, `Cratis.Chronicle.SubjectAttribute`, `IEventSequence.Redact`, `IReadModels.Release` |
21
+ | `Cratis.Chronicle.CodeAnalysis` | `18.3.0` | `CHR0026`, `CHR0034`, `CHR0035`, `CHR0043`, `CHR0046` |
22
22
 
23
23
  Reverify product sources before claiming support for another version.
24
24
 
@@ -224,7 +224,18 @@ storage, configured under `Cratis:Chronicle:Compliance:Encryption`.
224
224
  noise: which person owns the resulting document?
225
225
  - Bearer tokens, magic links, and signed URLs are secrets, not durable facts.
226
226
  Store a keyed hash or an opaque reference. Chronicle has no attribute that
227
- withholds a secret from the log.
227
+ withholds a secret from the log (Arc's `[NotAudited]` keeps a *command
228
+ property* out of the causation chain — see **cratis-arc-command** — but a
229
+ secret placed in an event payload is in the log for good).
230
+ - **Erasure is wider than the event log.** Crypto-shredding removes readability
231
+ of `[PII]` values in events and read models. It does nothing for a copy that
232
+ left Chronicle: an exported CSV, a generated PDF, a search index, an email
233
+ body, a blob the command uploaded, a database backup, or a restored
234
+ encryption-key backup that makes shredded ciphertext readable again. Model the
235
+ erasure request as a flow that covers those destinations too — and decide,
236
+ with the product owner, what happens when a backup is restored (re-erase on
237
+ restore, or a fence that refuses to restore keys). Those are product and legal
238
+ decisions; this skill only insists they are made, not left implicit.
228
239
  - If a subject boundary cannot be made person-level without changing product
229
240
  behavior, stop and surface the trade-off before implementing.
230
241
 
@@ -17,9 +17,11 @@ Constraints are discovered automatically. There is no registration call.
17
17
 
18
18
  | Package | Version | Purpose |
19
19
  | --- | --- | --- |
20
- | `Cratis.Chronicle` (client) | `16.45.2` | `[Unique]`, `[RemoveConstraint]`, `IConstraint`, `IConstraintBuilder`, `IUniqueConstraintBuilder`, `ConstraintViolation`, `IAppendResult` |
21
- | `Cratis.Chronicle` code analysis | `16.45.2` | `CHR0017`, `CHR0018`, `CHR0020` |
22
- | `Cratis.Chronicle.Testing` | `16.45.2` | `EventScenario` and the `IAppendResult` `Should*` assertions |
20
+ | `Cratis.Chronicle` (client) | `18.3.0` | `[Unique]`, `[RemoveConstraint]`, `IConstraint`, `IConstraintBuilder`, `IUniqueConstraintBuilder`, `ConstraintViolation`, `IAppendResult` |
21
+ | `Cratis.Chronicle` code analysis | `18.3.0` | `CHR0017`, `CHR0018`, `CHR0020` |
22
+ | `Cratis.Chronicle.Testing` | `18.3.0` | `EventScenario` and the `IAppendResult` `Should*` assertions |
23
+
24
+ > 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.
23
25
 
24
26
  Reverify against the Chronicle repository before claiming support for another
25
27
  version. Never translate an attribute argument order or an assertion name from
@@ -150,9 +152,14 @@ Note the asymmetry: `AppendResult` exposes a singular nullable
150
152
  `ConcurrencyViolations`. Only the `HasConcurrencyViolations` flag is on the
151
153
  interface.
152
154
 
153
- ⚠️ A `[PII]` property's value is encrypted before the constraint index sees it,
154
- so the claimed value and the violation details hold the encrypted form.
155
- Uniqueness still works; a human reading the index will not see the plaintext.
155
+ ⚠️ Constraints are evaluated and indexed against the **plaintext** of a `[PII]`
156
+ property, not its ciphertext — PII encryption is non-deterministic (a fresh key
157
+ and nonce per value), so hashing the encrypted form would never detect a
158
+ collision. The kernel builds a plaintext copy for the constraint check and
159
+ persists only the encrypted event; on reindex it releases (decrypts) stored
160
+ content to recompute the hashes, and skips subjects whose key has been erased so
161
+ they do not all collide on the hash of an empty value. The index therefore holds
162
+ plaintext-derived hashes, not the personal value itself.
156
163
 
157
164
  ## Step 6 — Specify the violation
158
165
 
@@ -25,8 +25,8 @@ This skill is verified against these exact sources:
25
25
 
26
26
  | Package | Version | Purpose |
27
27
  | --- | --- | --- |
28
- | `Cratis.Chronicle` | `16.45.2` | event types, event-source identities, subjects, `[PII]` |
29
- | `Cratis.Chronicle.CodeAnalysis` | `16.45.2` | `CHR0012`, `CHR0026`, `CHR0034`, `CHR0035` |
28
+ | `Cratis.Chronicle` | `18.3.0` | event types, event-source identities, subjects, `[PII]` |
29
+ | `Cratis.Chronicle.CodeAnalysis` | `18.3.0` | `CHR0012`, `CHR0026`, `CHR0034`, `CHR0035` |
30
30
 
31
31
  Reverify product sources before claiming support for another version.
32
32
 
@@ -14,8 +14,10 @@ application's constraints discovered and active.
14
14
 
15
15
  | Package | Version | Purpose |
16
16
  | --- | --- | --- |
17
- | `Cratis.Chronicle.Testing` | `16.45.2` | `EventScenario`, its `Given`/`When` builders, the `IAppendResult` `Should*` assertions |
18
- | `Cratis.Chronicle` | `16.45.2` | `IEventSequence.Append`/`AppendMany`, `IAppendResult`, `ConstraintViolation` |
17
+ | `Cratis.Chronicle.Testing` | `18.3.0` | `EventScenario`, its `Given`/`When` builders, the `IAppendResult` `Should*` assertions |
18
+ | `Cratis.Chronicle` | `18.3.0` | `IEventSequence.Append`/`AppendMany`, `IAppendResult`, `ConstraintViolation` |
19
+
20
+ > 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.
19
21
 
20
22
  ```bash
21
23
  dotnet add package Cratis.Chronicle.Testing
@@ -43,9 +45,13 @@ version. Never translate an assertion name from memory.
43
45
 
44
46
  ## When you do not
45
47
 
46
- - **Concurrency violations cannot be provoked here.** `EventScenario` wires a
47
- no-op concurrency-scope strategy, so `HasConcurrencyViolations` is never true
48
- inside the scenario however the append is shaped. Specify concurrency against
48
+ - **Concurrency violations are not what the scenario's defaults exercise.**
49
+ `EventScenario` wires a strategy that always answers `ConcurrencyScope.None`,
50
+ which the validator skips, so an append that lets the strategy pick the scope
51
+ (`When`, or `EventLog.Append` without a `concurrencyScope`) can never report
52
+ `HasConcurrencyViolations`. Only an append that passes an explicit scope on
53
+ `scenario.EventLog.Append(...)` reaches the in-process validator at all — and
54
+ that is a spec of the scope you hand-built, not of the application's strategy. Specify concurrency against
49
55
  the real kernel with an out-of-process integration specification, and do not
50
56
  write a scenario specification that looks like it covers it — it would pass
51
57
  vacuously, which is worse than not having one.
@@ -22,8 +22,8 @@ This skill is verified against these exact sources:
22
22
 
23
23
  | Package | Version | Purpose |
24
24
  | --- | --- | --- |
25
- | `Cratis.Chronicle` | `16.45.2` | `Cratis.Chronicle.Events` and `Cratis.Chronicle.Events.Migrations` |
26
- | `Cratis.Chronicle.CodeAnalysis` | `16.45.2` | `CHR0012`, `CHR0037`, `CHR0049`, `CHR0050` |
25
+ | `Cratis.Chronicle` | `18.3.0` | `Cratis.Chronicle.Events` and `Cratis.Chronicle.Events.Migrations` |
26
+ | `Cratis.Chronicle.CodeAnalysis` | `18.3.0` | `CHR0012`, `CHR0037`, `CHR0049`, `CHR0050` |
27
27
 
28
28
  Reverify product sources before claiming support for another version.
29
29
 
@@ -23,8 +23,8 @@ This skill is verified against this exact source:
23
23
 
24
24
  | Package | Version | Purpose |
25
25
  | --- | --- | --- |
26
- | `Cratis.Chronicle` | `16.45.2` | `EventStoreNamespaceName`, `IEventStoreNamespaceResolver`, `IChronicleClient.GetEventStore` |
27
- | `Cratis.Chronicle.AspNetCore` | `16.45.2` | the header and subdomain namespace resolvers |
26
+ | `Cratis.Chronicle` | `18.3.0` | `EventStoreNamespaceName`, `IEventStoreNamespaceResolver`, `IChronicleClient.GetEventStore` |
27
+ | `Cratis.Chronicle.AspNetCore` | `18.3.0` | the header and subdomain namespace resolvers |
28
28
 
29
29
  Reverify product sources before claiming support for another version.
30
30
 
@@ -17,7 +17,7 @@ This skill is verified against this exact source:
17
17
 
18
18
  | Package | Version | Purpose |
19
19
  | --- | --- | --- |
20
- | `Cratis.Chronicle` | `16.45.2` | `Cratis.Chronicle.Projections`, `Cratis.Chronicle.Projections.ModelBound`, `Cratis.Chronicle.Keys`, `Cratis.Chronicle.EventSequences` |
20
+ | `Cratis.Chronicle` | `18.3.0` | `Cratis.Chronicle.Projections`, `Cratis.Chronicle.Projections.ModelBound`, `Cratis.Chronicle.Keys`, `Cratis.Chronicle.EventSequences` |
21
21
 
22
22
  Reverify product sources before claiming support for another version.
23
23
 
@@ -90,6 +90,20 @@ a read model type is not supported and is not what the API means. If a read mode
90
90
  needs a field that only another read model has, the model is missing an event —
91
91
  fix the event model rather than cross-reading at runtime.
92
92
 
93
+ ### A joined property always wins — do not latch on it
94
+
95
+ A projection re-resolves its joins every time it handles one of the read model's
96
+ own events and applies the joined values **after** the local mappings, so a
97
+ property written both by a `From`/`[SetFrom]` and by a `Join` ends up with the
98
+ joined value regardless of the order the events arrived. A local write can
99
+ therefore never reset or clear a joined property. The natural latch shape — a
100
+ local event sets `IsBlocked = false`, a joined event sets it `true` — compiles,
101
+ reads correctly and silently sticks on the joined value. Give each fact its own
102
+ property (`BlockedAt`, `UnblockedAt`) and derive the outcome from both at the
103
+ query edge. Chronicle's analyzer **`CHR0042` (`JoinOverridesLocalWrite`)** reports
104
+ the overlap; suppress it only when the join is deliberately keeping that property
105
+ fresh.
106
+
93
107
  ## Selecting the source
94
108
 
95
109
  Class-level `[EventSequence("<name>")]`, `[EventLog]`, or `[EventStore("<name>")]`
@@ -121,9 +135,14 @@ or a reactor rather than annotating the projection.
121
135
  `key:` on **both** a parent and a nested or child type for the **same** event
122
136
  throws a duplicate-key exception at startup. Keep `[FromEvent<T>]` on the
123
137
  nested type only, or switch the nested type to property-level `[SetFrom<T>]`.
124
- - **Duplicate `[SetFromContext<T>]`.** Two properties with
125
- `[SetFromContext<SameEvent>]` on one read model crash at startup. Merge them,
126
- or use `[FromEvery]`.
138
+ - **Setting the same read-model property twice for one event.** The per-event
139
+ builder refuses a second `Set`/`Increment`/`Decrement`/`Add`/`Subtract` on a
140
+ property it already wrote for that event (`DuplicatePropertyInProjection`,
141
+ thrown while the projection is built at startup). Model-bound, that is two
142
+ attributes for the same `<TEvent>` on one property that both target it — for
143
+ example `[SetFrom<E>]` and `[Count<E>]` together. Several `[SetFromContext<E>]`
144
+ on one member is a different case: it is accepted and **all but the last are
145
+ discarded** — analyzer `CHR0040` reports it.
127
146
  - **Chaining after `AutoMap()`/`NoAutoMap()`.** Those two return the base builder
128
147
  interface, so `.NotRewindable()`, `.Passive()`, `.ContainerName()`, and
129
148
  `.FromEventSequence()` will not compile after them. Put them last.
@@ -1,6 +1,6 @@
1
1
  # The fluent projection builder
2
2
 
3
- Verified against `Cratis.Chronicle` `16.45.2`, namespace
3
+ Verified against `Cratis.Chronicle` `18.3.0`, namespace
4
4
  `Cratis.Chronicle.Projections`.
5
5
 
6
6
  ## The interfaces
@@ -1,6 +1,6 @@
1
1
  # Model-bound projection attributes
2
2
 
3
- Verified against `Cratis.Chronicle` `16.45.2`.
3
+ Verified against `Cratis.Chronicle` `18.3.0`.
4
4
 
5
5
  Unless noted, every attribute below lives in
6
6
  `Cratis.Chronicle.Projections.ModelBound`. Generic arity is stated exactly:
@@ -27,7 +27,7 @@ attribute takes no generic parameter at all.
27
27
  | --- | --- | --- | --- |
28
28
  | `[FromEvent<T>]` | `<T>` | `(string? key = default, string? parentKey = default)`, plus `ConstantKey { get; init; }` | class, struct; repeatable |
29
29
  | `[RemovedWith<T>]` | `<T>` | `(string? key = default, string? parentKey = default)` | class, struct, property, parameter; repeatable |
30
- | `[RemovedWithJoin<T>]` | `<T>` | `(string? key = default)` | property, parameter; repeatable |
30
+ | `[RemovedWithJoin<T>]` | `<T>` | `(string? key = default)` | class, struct, property, parameter; repeatable |
31
31
  | `[Nested]` | non-generic | none | property, parameter; single |
32
32
  | `[FromAll]` | non-generic | `(string? contextProperty = default, string? property = default)` | **property only**; single |
33
33
  | `[FromEvery]` | non-generic | `(string? property = default, string? contextProperty = default)` | property, parameter; single |
@@ -24,8 +24,8 @@ This skill is verified against these exact sources:
24
24
 
25
25
  | Package | Version | Purpose |
26
26
  | --- | --- | --- |
27
- | `Cratis.Chronicle` | `16.45.2` | `Cratis.Chronicle.Reactors`, `Cratis.Chronicle.Reactors.SideEffects`, `Cratis.Chronicle.EventSequences`, `Cratis.Chronicle.Events` |
28
- | `Cratis.Chronicle.CodeAnalysis` | `16.45.2` | `CHR0004`, `CHR0005`, `CHR0008`, `CHR0013`, `CHR0022`, `CHR0031`, `CHR0032` |
27
+ | `Cratis.Chronicle` | `18.3.0` | `Cratis.Chronicle.Reactors`, `Cratis.Chronicle.Reactors.SideEffects`, `Cratis.Chronicle.EventSequences`, `Cratis.Chronicle.Events` |
28
+ | `Cratis.Chronicle.CodeAnalysis` | `18.3.0` | `CHR0004`, `CHR0005`, `CHR0008`, `CHR0013`, `CHR0022`, `CHR0031`, `CHR0032` |
29
29
 
30
30
  Reverify product sources before claiming support for another version.
31
31
 
@@ -280,19 +280,33 @@ selection, a reactor or a reducer is where it happens.
280
280
  ## Translation via a command
281
281
 
282
282
  A translation that adapts one area's events into another's intent runs a command
283
- rather than appending directly. `ICommandPipeline` is an **Arc** type
284
- (`Cratis.Arc.Commands`), not a Chronicle one:
283
+ rather than appending directly. With the Arc Chronicle integration the handler
284
+ **returns** the command — the same shape as returning an event — and Arc executes
285
+ it through validation and authorization; a denied, invalid or throwing result is a
286
+ side-effect failure that fails the partition rather than being dropped:
285
287
 
286
288
  ```csharp
287
- using Cratis.Arc.Commands;
289
+ using Cratis.Arc.Chronicle.Reactors;
288
290
 
289
- public class <ReactorName>(ICommandPipeline commandPipeline) : IReactor
291
+ [ExecuteCommandsAsSystem("<role the command requires>")] // only when the command is [Roles]/[Authorize]-gated
292
+ public class <ReactorName> : IReactor
290
293
  {
291
- public async Task <MethodName>(<EventName> @event, EventContext context) =>
292
- await commandPipeline.Execute(new <CommandName>(@event.<Property>));
294
+ [OnceOnly]
295
+ public Task<<CommandName>> <MethodName>(<EventName> @event, EventContext context) =>
296
+ Task.FromResult(new <CommandName>(@event.<Property>));
293
297
  }
294
298
  ```
295
299
 
300
+ A reactor runs with **no principal**; `[ExecuteCommandsAsSystem]` (class-level,
301
+ `Cratis.Arc.Chronicle.Reactors`) supplies one for the **returned** commands only.
302
+ A collection return is executed as commands when every element is a command.
303
+
304
+ The imperative form — inject `ICommandPipeline` (`Cratis.Arc.Commands`) and call
305
+ `Execute(command)` — is still supported and is the subject of the
306
+ **cratis-arc-command-execution** skill. It needs `[OnceOnly]` as well (`ARCCHR0006`
307
+ warns), the `CommandResult` must be inspected, and `[ExecuteCommandsAsSystem]`
308
+ does not cover it.
309
+
296
310
  ## Failure behavior
297
311
 
298
312
  If a handler throws, or a returned side-effect event fails to append — a
@@ -23,7 +23,7 @@ This skill is verified against these exact sources:
23
23
 
24
24
  | Package | Version | Purpose |
25
25
  | --- | --- | --- |
26
- | `Cratis.Chronicle` | `16.45.2` | `Cratis.Chronicle.Events`, `Cratis.Chronicle.Keys`, `Cratis.Chronicle.ReadModels` |
26
+ | `Cratis.Chronicle` | `18.3.0` | `Cratis.Chronicle.Events`, `Cratis.Chronicle.Keys`, `Cratis.Chronicle.ReadModels` |
27
27
  | `Cratis.Arc.Core` | current | `Cratis.Arc.Queries.ModelBound.ReadModelAttribute` and `PathAttribute` |
28
28
  | `Cratis.Arc.MongoDB` | current | the `Observe` family of `IMongoCollection<T>` extensions |
29
29
 
@@ -1,7 +1,7 @@
1
1
  # Read-model queries
2
2
 
3
3
  Verified against `Cratis.Arc.Core` and `Cratis.Arc.MongoDB` as checked out
4
- alongside `Cratis.Chronicle` `16.45.2`. Confirm the exact Arc package version in
4
+ alongside `Cratis.Chronicle` `18.3.0`. Confirm the exact Arc package version in
5
5
  the consuming project before citing one.
6
6
 
7
7
  ## Where the pieces live
@@ -17,9 +17,11 @@ separate `When`: seeding the events is what makes the projection run.
17
17
 
18
18
  | Package | Version | Purpose |
19
19
  | --- | --- | --- |
20
- | `Cratis.Chronicle.Testing` | `16.45.2` | `ReadModelScenario<TReadModel>` and its `Given` builder |
20
+ | `Cratis.Chronicle.Testing` | `18.3.0` | `ReadModelScenario<TReadModel>` and its `Given` builder |
21
21
  | `Cratis.Specifications.XUnit` | `4.x` | The `Specification` base and the `ShouldXxx` assertions |
22
22
 
23
+ > 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.
24
+
23
25
  ```bash
24
26
  dotnet add package Cratis.Chronicle.Testing
25
27
  ```
@@ -17,7 +17,7 @@ This skill is verified against this exact source:
17
17
 
18
18
  | Package | Version | Purpose |
19
19
  | --- | --- | --- |
20
- | `Cratis.Chronicle` | `16.45.2` | `Cratis.Chronicle.Reducers`, `Cratis.Chronicle.ReadModels`, `Cratis.Chronicle.Events` |
20
+ | `Cratis.Chronicle` | `18.3.0` | `Cratis.Chronicle.Reducers`, `Cratis.Chronicle.ReadModels`, `Cratis.Chronicle.Events` |
21
21
 
22
22
  Reverify product sources before claiming support for another version.
23
23