@cratis/pi 2.21.3 → 2.23.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 (102) 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/subagent/agents.ts +52 -7
  14. package/package/corpus/harnesses/pi/extensions/subagent/index.ts +14 -0
  15. package/package/corpus/hooks/README.md +29 -27
  16. package/package/corpus/hooks/agent-stop.md +1 -5
  17. package/package/corpus/hooks/scripts/cratis-patterns.json +1 -1
  18. package/package/corpus/hooks/scripts/validate-package-subpaths.sh +1 -2
  19. package/package/corpus/prompts/verify-ai-setup.prompt.md +17 -7
  20. package/package/corpus/rules/ai-distribution.md +58 -0
  21. package/package/corpus/rules/components.md +77 -44
  22. package/package/corpus/rules/dialogs.md +24 -20
  23. package/package/corpus/rules/efcore.md +35 -187
  24. package/package/corpus/rules/framework.md +1 -1
  25. package/package/corpus/rules/frontend-quality.md +1 -1
  26. package/package/corpus/rules/frontend-testing.md +4 -4
  27. package/package/corpus/rules/general.md +29 -105
  28. package/package/corpus/rules/new-repository-intake.md +34 -0
  29. package/package/corpus/rules/orleans.md +13 -27
  30. package/package/corpus/rules/react.md +12 -12
  31. package/package/corpus/rules/reactors.md +28 -5
  32. package/package/corpus/rules/specs.scenarios.csharp.md +18 -3
  33. package/package/corpus/rules/specs.typescript.md +1 -1
  34. package/package/corpus/rules/storybook.md +2 -2
  35. package/package/corpus/rules/typescript.md +1 -0
  36. package/package/corpus/rules/vertical-slices.md +20 -15
  37. package/package/corpus/rules/writing-correct-examples.md +1 -0
  38. package/package/corpus/skills/cratis-application-react-specifications/SKILL.md +4 -4
  39. package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +16 -8
  40. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +6 -4
  41. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +1 -1
  42. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +1 -1
  43. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +1 -1
  44. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +1 -1
  45. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +7 -4
  46. package/package/corpus/skills/cratis-arc-command/SKILL.md +9 -7
  47. package/package/corpus/skills/cratis-arc-command/references/command-result.md +1 -1
  48. package/package/corpus/skills/cratis-arc-command/references/handler-shapes.md +2 -2
  49. package/package/corpus/skills/cratis-arc-command/references/proxy-generation.md +6 -3
  50. package/package/corpus/skills/cratis-arc-command/references/read-model-injection.md +1 -1
  51. package/package/corpus/skills/cratis-arc-command-execution/SKILL.md +12 -2
  52. package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +5 -3
  53. package/package/corpus/skills/cratis-arc-ef-core-migration/SKILL.md +7 -4
  54. package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +10 -3
  55. package/package/corpus/skills/cratis-arc-query-paging/SKILL.md +13 -8
  56. package/package/corpus/skills/cratis-arc-react-page/SKILL.md +27 -24
  57. package/package/corpus/skills/cratis-arc-react-page/references/data-page.md +15 -11
  58. package/package/corpus/skills/cratis-arc-react-page/references/data-tables.md +9 -2
  59. package/package/corpus/skills/cratis-arc-react-page/references/dialogs.md +11 -9
  60. package/package/corpus/skills/cratis-arc-react-page/references/queries-and-commands.md +26 -2
  61. package/package/corpus/skills/cratis-chronicle-cli-operations/SKILL.md +19 -13
  62. package/package/corpus/skills/cratis-chronicle-client-dotnet/SKILL.md +17 -15
  63. package/package/corpus/skills/cratis-chronicle-client-elixir/SKILL.md +24 -20
  64. package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +26 -15
  65. package/package/corpus/skills/cratis-chronicle-client-typescript/SKILL.md +15 -11
  66. package/package/corpus/skills/cratis-chronicle-compliance/SKILL.md +14 -3
  67. package/package/corpus/skills/cratis-chronicle-event-constraints/SKILL.md +13 -6
  68. package/package/corpus/skills/cratis-chronicle-event-modeling/SKILL.md +2 -2
  69. package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +11 -5
  70. package/package/corpus/skills/cratis-chronicle-event-type-migration/SKILL.md +2 -2
  71. package/package/corpus/skills/cratis-chronicle-multi-tenancy/SKILL.md +2 -2
  72. package/package/corpus/skills/cratis-chronicle-projection/SKILL.md +23 -4
  73. package/package/corpus/skills/cratis-chronicle-projection/references/fluent-builder.md +1 -1
  74. package/package/corpus/skills/cratis-chronicle-projection/references/model-bound-attributes.md +2 -2
  75. package/package/corpus/skills/cratis-chronicle-reactor/SKILL.md +22 -8
  76. package/package/corpus/skills/cratis-chronicle-read-model/SKILL.md +1 -1
  77. package/package/corpus/skills/cratis-chronicle-read-model/references/queries.md +1 -1
  78. package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +3 -1
  79. package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +1 -1
  80. package/package/corpus/skills/cratis-chronicle-web-workbench/SKILL.md +19 -11
  81. package/package/corpus/skills/cratis-cli-terminal-workbench/SKILL.md +11 -6
  82. package/package/corpus/skills/cratis-code-review/SKILL.md +8 -6
  83. package/package/corpus/skills/cratis-components-accessibility/SKILL.md +112 -82
  84. package/package/corpus/skills/cratis-components-schema-editor/SKILL.md +12 -10
  85. package/package/corpus/skills/cratis-components-stepper-command-dialog/SKILL.md +11 -12
  86. package/package/corpus/skills/cratis-components-styling/SKILL.md +204 -170
  87. package/package/corpus/skills/cratis-components-toolbar/SKILL.md +32 -26
  88. package/package/corpus/skills/cratis-event-model-diagram/SKILL.md +1 -1
  89. package/package/corpus/skills/cratis-fundamentals-concept/SKILL.md +2 -2
  90. package/package/corpus/skills/cratis-fundamentals-type-discovery/SKILL.md +2 -2
  91. package/package/corpus/skills/cratis-lens-browser-extension/SKILL.md +4 -2
  92. package/package/corpus/skills/cratis-performance-review/SKILL.md +4 -2
  93. package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +15 -5
  94. package/package/corpus/skills/cratis-security-review/SKILL.md +20 -3
  95. package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +3 -3
  96. package/package/corpus/skills/cratis-specifications-csharp/references/application-scenarios.md +12 -8
  97. package/package/corpus/skills/cratis-specifications-csharp/references/csharp-patterns.md +1 -1
  98. package/package/corpus/skills/cratis-specifications-csharp/references/integration-specs.md +7 -3
  99. package/package/corpus/skills/cratis-specifications-typescript/SKILL.md +6 -3
  100. package/package/corpus/skills/cratis-specifications-typescript/references/typescript-patterns.md +21 -4
  101. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +29 -17
  102. package/package.json +1 -1
@@ -16,8 +16,10 @@ This skill is verified against these exact sources:
16
16
 
17
17
  | Package | Version | Purpose |
18
18
  | --- | --- | --- |
19
- | `Cratis.Cli` | `2.4.0` | the `cratis` command, its contexts, output formats, and command tree |
20
- | `Cratis.Chronicle` | `16.45.2` | the observer, partition, sequence, and job concepts the CLI reads |
19
+ | `Cratis.Cli` | `3.2.0` | the `cratis` command, its contexts, output formats, and command tree |
20
+
21
+ > Re-verified at `Cratis/cli` **v3.2.0** (originally written against 2.4.0): every command, branch, flag and output format this skill names exists there; the 2.x→3.x change is the Chronicle 17/18 wire migration plus new branches (`ai`, `arc`, `screenplay`, `prologue`, `llm`) this skill does not cover.
22
+ | `Cratis.Chronicle` | `18.3.0` | the observer, partition, sequence, and job concepts the CLI reads |
21
23
 
22
24
  The CLI ships a **versioned machine-readable catalog of its own surface**, and
23
25
  that catalog is the authority for exact command names on the installed version:
@@ -110,7 +112,7 @@ error.
110
112
  | Is the server healthy at all? | `cratis chronicle diagnose` (exit code 3 when unhealthy) |
111
113
  | Why has this read model stopped updating? | `chronicle failed-partitions list --observer <id>` — the error is on the partition, not in the log |
112
114
  | An observer is quarantined — why? | `chronicle failed-partitions show <observer> <partition> --detailed`, with attempt history |
113
- | Did this event actually get appended? | `chronicle events get --event-type <type> --event-source-id <id>`, or `chronicle events tail` |
115
+ | Did this event actually get appended? | `chronicle events get --event-type <type> --event-source-id <id>` (`events tail` returns only the highest sequence number — not a count, not the events) |
114
116
  | What is this event's shape in the store? | `chronicle event-types list`, then `chronicle event-types show <type>` |
115
117
  | Is this projection registered? | `chronicle projections list`, then `chronicle projections show <id>` |
116
118
  | What does the read model actually hold? | `chronicle read-models get <read-model> <key>`, or `read-models instances` |
@@ -165,16 +167,20 @@ Before any of them:
165
167
 
166
168
  ### Two confirmation traps
167
169
 
168
- **Confirmation prompts do not fire when stdout is not a terminal.** The CLI
169
- treats a non-interactive session as an implicit yes, so a destructive command
170
- run from a script, a pipeline, or an agent harness proceeds without `-y` and
171
- without asking. Never rely on the prompt as the safety mechanism — the
172
- authorization above is the safety mechanism.
173
-
174
- **Not every mutating command prompts, even interactively.** `jobs stop` and
175
- `jobs resume` describe themselves as prompting for confirmation, but no prompt
176
- exists in the implementation. `users add`, `applications add`, and
177
- `subscriptions add` do not prompt either.
170
+ **Destructive commands fail closed outside a terminal.** Every command with a
171
+ confirmation prompt — `observers replay`, `observers replay-partition`,
172
+ `observers retry-partition`, `observers clear-quarantine`, `jobs stop`,
173
+ `jobs resume`, `recommendations perform`, `recommendations ignore`,
174
+ `users remove`, `applications remove`, `subscriptions remove` — asks
175
+ interactively, and when the session is **not** interactive (a script, a
176
+ pipeline, an agent harness) it does *not* proceed: it writes
177
+ "Re-run the command with --yes to confirm the operation" and exits with the
178
+ validation error code. `--yes` is therefore the only way a non-interactive run
179
+ performs one of these, which makes `--yes` the authorization boundary. Declining
180
+ the interactive prompt exits successfully having done nothing.
181
+
182
+ **Additive commands do not prompt.** `users add`, `applications add` and
183
+ `subscriptions add` run without confirmation in every mode.
178
184
 
179
185
  Reaching for `--yes` to silence a prompt you have not read is how the wrong
180
186
  store gets replayed. A failed partition you have not yet explained is not a
@@ -17,16 +17,18 @@ There is no host requirement and no application architecture imposed.
17
17
  ## Verified product sources
18
18
 
19
19
  This skill is verified against the `Cratis/Chronicle` repository at tag
20
- `v17.0.1`, whose client sources are byte-identical to `16.45.2` for every public
21
- type cited here except the compatibility check noted below.
20
+ `v18.3.0`. Its client sources are unchanged in public shape since `v17.0.1`
21
+ (the 17→18 client diff is converters, options and doc comments), and `v17.0.1`
22
+ was byte-identical to `16.45.2` for every public type cited here except the
23
+ compatibility check noted below.
22
24
 
23
25
  | Package | Line | Purpose |
24
26
  | --- | --- | --- |
25
- | `Cratis.Chronicle` | `17.x` | `ChronicleClient`, `IEventStore`, `[EventType]`, observers |
26
- | `Cratis.Chronicle.Connections` | `17.x` | `ChronicleConnectionString`, connection lifecycle |
27
- | `Cratis.Chronicle.AspNetCore` | `17.x` | ASP.NET Core wiring, header/subdomain namespace resolvers |
28
- | `Cratis.Chronicle.Testing` | `17.x` | in-process scenarios |
29
- | `Cratis.Chronicle.CodeAnalysis` | `17.x` | the analyzers, a **separate opt-in** package |
27
+ | `Cratis.Chronicle` | `18.3.0` | `ChronicleClient`, `IEventStore`, `[EventType]`, observers |
28
+ | `Cratis.Chronicle.Connections` | `18.3.0` | `ChronicleConnectionString`, connection lifecycle |
29
+ | `Cratis.Chronicle.AspNetCore` | `18.3.0` | ASP.NET Core wiring, header/subdomain namespace resolvers |
30
+ | `Cratis.Chronicle.Testing` | `18.3.0` | in-process scenarios |
31
+ | `Cratis.Chronicle.CodeAnalysis` | `18.3.0` | the analyzers, a **separate opt-in** package |
30
32
 
31
33
  Take the exact version from nuget.org. The version in source is a `1.0.0`
32
34
  placeholder injected at pack time (`Source/Directory.Build.props`), so the
@@ -182,23 +184,23 @@ and an optional `EventStoreNamespaceResolverType`.
182
184
  Since Chronicle 17 the compatibility check is a server-side RPC, and **the client
183
185
  runs it automatically inside `Connect()`** —
184
186
  `Source/Clients/Connections/ChronicleConnection.cs:310` calls
185
- `CheckCompatibility` (`:405`), sending the client type, client version, protocol
186
- version, and the descriptor set its contracts package was built with (`:411-417`).
187
- The rationale is at `:400-404`: Chronicle has clients in four languages and only
187
+ `CheckCompatibility` (`:414`), sending the client type, client version, protocol
188
+ version, and the descriptor set its contracts package was built with (`:420-426`).
189
+ The rationale is at `:409-413`: Chronicle has clients in four languages and only
188
190
  some can build a descriptor set at runtime, so each ships the one it was built
189
191
  with and the server does the single comparison.
190
192
 
191
193
  Behavior you can rely on:
192
194
 
193
195
  - A server too old to have the RPC answers `Unimplemented`, and the client falls
194
- back to the previous client-side exchange (`:419-425`) — upgrading the client
196
+ back to the previous client-side exchange (`:428-434`) — upgrading the client
195
197
  does not silently drop the check.
196
- - Any other transport error is **logged and ignored** (`:426-432`), on the stated
198
+ - Any other transport error is **logged and ignored** (`:435-441`), on the stated
197
199
  reasoning that failing to ask says nothing about whether the two sides agree.
198
200
  - A genuine mismatch throws `IncompatibleServerException`
199
201
  (`Source/Clients/Connections/IncompatibleServerException.cs:10`) whose message
200
202
  names the server address, its version, its protocol version, and the specific
201
- incompatibilities (`:436-443`).
203
+ incompatibilities (`:443-449`).
202
204
 
203
205
  The client identifies itself as `".NET"`
204
206
  (`Source/Clients/Connections/ChronicleClientIdentity.cs:22`) with its assembly
@@ -333,8 +335,8 @@ Read models are queried through `eventStore.ReadModels`
333
335
  Artifacts are found by **assembly scanning**, with no registration call and no DI
334
336
  container required. `DefaultClientArtifactsProvider.Default` composes the
335
337
  project-referenced and package-referenced assemblies
336
- (`Source/Clients/DotNET/DefaultClientArtifactsProvider.cs:35`), and the
337
- predicates are exactly (`:230-238`):
338
+ (`Source/Clients/DotNET/DefaultClientArtifactsProvider.cs:37`), and the
339
+ predicates are exactly (`:242-251`):
338
340
 
339
341
  | Kind | Predicate |
340
342
  | --- | --- |
@@ -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 |