@cratis/pi 7.34.1 → 7.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package/corpus/agents/security-reviewer.md +1 -0
- package/package/corpus/agents/spec-writer.md +1 -1
- package/package/corpus/harnesses/pi/extensions/shared/rules.ts +3 -1
- package/package/corpus/hooks/scripts/cratis-nuget-pins.txt +7 -6
- package/package/corpus/hooks/scripts/cratis-release-notes-reviewed.cjs +7 -6
- package/package/corpus/hooks/scripts/cratis-store-mutations.json +7 -3
- package/package/corpus/product-sync.json +896 -0
- package/package/corpus/prompts/add-business-rule.prompt.md +1 -1
- package/package/corpus/prompts/ship-changes.prompt.md +2 -2
- package/package/corpus/rules/ai-distribution.md +31 -3
- package/package/corpus/rules/application-profile.md +2 -1
- package/package/corpus/rules/components.md +66 -7
- package/package/corpus/rules/concepts.md +11 -3
- package/package/corpus/rules/csharp.md +3 -1
- package/package/corpus/rules/dialogs.md +9 -0
- package/package/corpus/rules/efcore.md +1 -1
- package/package/corpus/rules/elixir.md +49 -0
- package/package/corpus/rules/framework.md +5 -3
- package/package/corpus/rules/general.md +1 -0
- package/package/corpus/rules/glossary.md +4 -4
- package/package/corpus/rules/go-engineering.md +60 -0
- package/package/corpus/rules/go.md +115 -0
- package/package/corpus/rules/java.md +5 -2
- package/package/corpus/rules/kotlin.md +7 -5
- package/package/corpus/rules/orleans.md +65 -9
- package/package/corpus/rules/profiles.md +43 -6
- package/package/corpus/rules/pull-requests.md +3 -3
- package/package/corpus/rules/react.md +8 -1
- package/package/corpus/rules/reactors.md +27 -3
- package/package/corpus/rules/specs.csharp.md +11 -4
- package/package/corpus/rules/specs.scenarios.csharp.md +35 -4
- package/package/corpus/rules/specs.typescript.md +2 -1
- package/package/corpus/rules/storybook.md +1 -1
- package/package/corpus/rules/typescript.md +1 -1
- package/package/corpus/rules/vertical-slices.md +37 -5
- package/package/corpus/skills/cratis-application-react-specifications/SKILL.md +4 -2
- package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +18 -8
- package/package/corpus/skills/cratis-application-slice-specifications/references/query-and-decision-scenarios.md +65 -0
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +53 -57
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +38 -42
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +18 -7
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authproxy.md +30 -0
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +12 -9
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +9 -9
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/observable-emission-guards.md +8 -5
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +20 -4
- package/package/corpus/skills/cratis-arc-command/SKILL.md +32 -10
- package/package/corpus/skills/cratis-arc-command/references/command-result.md +12 -5
- package/package/corpus/skills/cratis-arc-command/references/decisions-and-routing.md +95 -0
- package/package/corpus/skills/cratis-arc-command/references/generated-values.md +3 -2
- package/package/corpus/skills/cratis-arc-command/references/handler-shapes.md +8 -8
- package/package/corpus/skills/cratis-arc-command/references/proxy-generation.md +22 -3
- package/package/corpus/skills/cratis-arc-command/references/read-model-injection.md +13 -1
- package/package/corpus/skills/cratis-arc-command-execution/SKILL.md +19 -9
- package/package/corpus/skills/cratis-arc-command-kotlin/SKILL.md +44 -18
- package/package/corpus/skills/cratis-arc-command-kotlin/references/read-model-naming.md +29 -0
- package/package/corpus/skills/cratis-arc-command-operation/SKILL.md +10 -9
- package/package/corpus/skills/cratis-arc-command-operation/references/execution-and-recovery.md +2 -2
- package/package/corpus/skills/cratis-arc-command-operation/references/testing.md +4 -3
- package/package/corpus/skills/cratis-arc-command-typescript/SKILL.md +42 -22
- package/package/corpus/skills/cratis-arc-command-typescript/references/chronicle-events.md +36 -5
- package/package/corpus/skills/cratis-arc-command-typescript/references/command-operations.md +114 -0
- package/package/corpus/skills/cratis-arc-command-typescript/references/hosting-and-storage.md +37 -16
- package/package/corpus/skills/cratis-arc-command-typescript/references/identity-and-introspection.md +88 -0
- package/package/corpus/skills/cratis-arc-command-typescript/references/observability-and-host-scopes.md +25 -0
- package/package/corpus/skills/cratis-arc-command-typescript/references/proxy-generation.md +35 -4
- package/package/corpus/skills/cratis-arc-command-typescript/references/slice-layout.md +11 -0
- package/package/corpus/skills/cratis-arc-command-typescript/verification.json +8 -1
- package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +49 -19
- package/package/corpus/skills/cratis-arc-ef-core-migration/SKILL.md +12 -6
- package/package/corpus/skills/cratis-arc-go/SKILL.md +148 -0
- package/package/corpus/skills/cratis-arc-go/references/queries.md +80 -0
- package/package/corpus/skills/cratis-arc-hosting/SKILL.md +96 -0
- package/package/corpus/skills/cratis-arc-hosting/references/integrations-and-publish.md +80 -0
- package/package/corpus/skills/cratis-arc-identity-and-tenancy-kotlin/SKILL.md +137 -0
- package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +20 -2
- package/package/corpus/skills/cratis-arc-query-kotlin/SKILL.md +26 -6
- package/package/corpus/skills/cratis-arc-query-paging/SKILL.md +25 -20
- package/package/corpus/skills/cratis-arc-query-typescript/SKILL.md +32 -6
- package/package/corpus/skills/cratis-arc-query-typescript/references/storage-observation-and-compliance.md +66 -0
- package/package/corpus/skills/cratis-arc-query-typescript/verification.json +5 -1
- package/package/corpus/skills/cratis-arc-react-page/SKILL.md +24 -13
- package/package/corpus/skills/cratis-arc-react-page/references/data-page.md +20 -10
- package/package/corpus/skills/cratis-arc-react-page/references/data-tables.md +17 -3
- package/package/corpus/skills/cratis-arc-react-page/references/dialogs.md +14 -3
- package/package/corpus/skills/cratis-arc-react-page/references/from-screenplay.md +6 -1
- package/package/corpus/skills/cratis-arc-react-page/references/provenance.md +1 -1
- package/package/corpus/skills/cratis-arc-react-page/references/queries-and-commands.md +11 -1
- package/package/corpus/skills/cratis-arc-react-page/verification.json +6 -1
- package/package/corpus/skills/cratis-arc-spring-data-kotlin/SKILL.md +163 -0
- package/package/corpus/skills/cratis-arc-testing-kotlin/SKILL.md +131 -0
- package/package/corpus/skills/cratis-arc-validation-kotlin/SKILL.md +44 -11
- package/package/corpus/skills/cratis-arc-validation-typescript/SKILL.md +5 -3
- package/package/corpus/skills/cratis-chronicle-cli-operations/SKILL.md +79 -25
- package/package/corpus/skills/cratis-chronicle-cli-operations/references/command-effects.md +57 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/SKILL.md +87 -99
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/additional-surfaces.md +65 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/client-construction.md +42 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/compatibility-check.md +12 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/discovery.md +43 -19
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/hosting.md +29 -1
- package/package/corpus/skills/cratis-chronicle-client-elixir/SKILL.md +158 -162
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/append-api.md +36 -15
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/compliance.md +32 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/connections.md +56 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/event-sources.md +64 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/pitfalls.md +25 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/projections.md +48 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/upgrading.md +34 -0
- package/package/corpus/skills/cratis-chronicle-client-elixir/references/variants.md +13 -5
- package/package/corpus/skills/cratis-chronicle-client-elixir/verification.json +23 -0
- package/package/corpus/skills/cratis-chronicle-client-go/SKILL.md +162 -0
- package/package/corpus/skills/cratis-chronicle-client-go/references/projections.md +73 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +87 -88
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/connection-lifecycle.md +13 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/discovery-and-registration.md +14 -4
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/event-sources.md +65 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/options.md +29 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/spring-boot.md +13 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/references/variants.md +5 -4
- package/package/corpus/skills/cratis-chronicle-client-typescript/SKILL.md +165 -156
- package/package/corpus/skills/cratis-chronicle-client-typescript/references/read-models-and-projections.md +54 -11
- package/package/corpus/skills/cratis-chronicle-client-typescript/references/testing.md +126 -0
- package/package/corpus/skills/cratis-chronicle-client-typescript/references/variants.md +3 -5
- package/package/corpus/skills/cratis-chronicle-client-typescript/verification.json +19 -0
- package/package/corpus/skills/cratis-chronicle-compliance/SKILL.md +36 -13
- package/package/corpus/skills/cratis-chronicle-decision-reads/SKILL.md +110 -0
- package/package/corpus/skills/cratis-chronicle-decision-reads/references/admission-and-ownership.md +57 -0
- package/package/corpus/skills/cratis-chronicle-decision-reads/verification.json +12 -0
- package/package/corpus/skills/cratis-chronicle-event-constraints/SKILL.md +58 -19
- package/package/corpus/skills/cratis-chronicle-event-constraints/references/closing-streams.md +73 -0
- package/package/corpus/skills/cratis-chronicle-event-modeling/SKILL.md +32 -3
- package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +18 -5
- package/package/corpus/skills/cratis-chronicle-event-type-migration/SKILL.md +32 -2
- package/package/corpus/skills/cratis-chronicle-hosting/SKILL.md +134 -0
- package/package/corpus/skills/cratis-chronicle-hosting/references/operations.md +89 -0
- package/package/corpus/skills/cratis-chronicle-hosting/verification.json +14 -0
- package/package/corpus/skills/cratis-chronicle-mcp-inspection/SKILL.md +93 -66
- package/package/corpus/skills/cratis-chronicle-mcp-inspection/references/blocked-tools.md +21 -7
- package/package/corpus/skills/cratis-chronicle-mcp-inspection/references/deployment.md +77 -0
- package/package/corpus/skills/cratis-chronicle-mcp-inspection/references/observational-tools.md +46 -4
- package/package/corpus/skills/cratis-chronicle-multi-tenancy/SKILL.md +15 -1
- package/package/corpus/skills/cratis-chronicle-projection/SKILL.md +44 -10
- package/package/corpus/skills/cratis-chronicle-projection/references/fluent-builder.md +7 -1
- package/package/corpus/skills/cratis-chronicle-projection/references/model-bound-attributes.md +8 -0
- package/package/corpus/skills/cratis-chronicle-reactor/SKILL.md +85 -105
- package/package/corpus/skills/cratis-chronicle-reactor/references/specifications.md +20 -1
- package/package/corpus/skills/cratis-chronicle-read-model/SKILL.md +2 -2
- package/package/corpus/skills/cratis-chronicle-read-model/references/queries.md +35 -16
- package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +15 -3
- package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +23 -4
- package/package/corpus/skills/cratis-chronicle-web-workbench/SKILL.md +40 -15
- package/package/corpus/skills/cratis-cli-terminal-workbench/SKILL.md +20 -6
- package/package/corpus/skills/cratis-code-review/SKILL.md +25 -3
- package/package/corpus/skills/cratis-components-accessibility/SKILL.md +20 -4
- package/package/corpus/skills/cratis-components-schema-editor/SKILL.md +40 -28
- package/package/corpus/skills/cratis-components-schema-editor/references/tree-layout.md +62 -0
- package/package/corpus/skills/cratis-components-schema-editor/verification.json +14 -0
- package/package/corpus/skills/cratis-components-stepper-command-dialog/SKILL.md +16 -8
- package/package/corpus/skills/cratis-components-styling/SKILL.md +57 -20
- package/package/corpus/skills/cratis-components-styling/verification.json +13 -0
- package/package/corpus/skills/cratis-components-toolbar/SKILL.md +37 -7
- package/package/corpus/skills/cratis-components-toolbar/verification.json +12 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/SKILL.md +6 -0
- package/package/corpus/skills/cratis-engineering-csharp-conventions/references/exceptions-logging-and-di.md +5 -3
- package/package/corpus/skills/cratis-fundamentals-concept/SKILL.md +19 -6
- package/package/corpus/skills/cratis-fundamentals-concept/references/typescript-concepts.md +97 -0
- package/package/corpus/skills/cratis-fundamentals-concept/verification.json +4 -1
- package/package/corpus/skills/cratis-fundamentals-concept-go/SKILL.md +112 -0
- package/package/corpus/skills/cratis-fundamentals-type-discovery/SKILL.md +54 -17
- package/package/corpus/skills/cratis-fundamentals-type-discovery/verification.json +14 -0
- package/package/corpus/skills/cratis-lens-browser-extension/SKILL.md +55 -28
- package/package/corpus/skills/cratis-performance-review/SKILL.md +5 -2
- package/package/corpus/skills/cratis-release-notes/SKILL.md +3 -1
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/SKILL.md +5 -4
- package/package/corpus/skills/cratis-screenplay-command-surface/SKILL.md +5 -6
- package/package/corpus/skills/cratis-screenplay-command-surface/references/compliance.md +24 -6
- package/package/corpus/skills/cratis-screenplay-command-surface/references/enum-numbers.md +4 -2
- package/package/corpus/skills/cratis-screenplay-command-surface/references/generated-values-and-responses.md +4 -1
- package/package/corpus/skills/cratis-screenplay-event-modeling/SKILL.md +3 -2
- package/package/corpus/skills/cratis-screenplay-event-modeling/references/parsed-not-runnable.md +2 -2
- package/package/corpus/skills/cratis-screenplay-legacy-extraction/SKILL.md +1 -0
- package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/generate-notes.md +34 -4
- package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +5 -3
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md +1 -1
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-tools.md +4 -2
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/property-links.md +42 -3
- package/package/corpus/skills/cratis-screenplay-model-authoring/verification.json +3 -1
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/gap-fill-handoff.md +3 -1
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/SKILL.md +3 -1
- package/package/corpus/skills/cratis-screenplay-specifications/references/examples-and-routes.md +48 -0
- package/package/corpus/skills/cratis-screenplay-specifications/verification.json +3 -1
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/SKILL.md +4 -2
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/streams-example.md +5 -4
- package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +8 -6
- package/package/corpus/skills/cratis-screenplay-toolchain/references/cheat-sheet-example.md +2 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/references/diagnostics.md +4 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/references/doc-contradictions.md +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/executable-subset.md +16 -11
- package/package/corpus/skills/cratis-screenplay-toolchain/references/renderable-subset.md +5 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +1329 -32
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/verdicts.md +2 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/references/versions.md +23 -7
- package/package/corpus/skills/cratis-screenplay-toolchain/verification.json +1 -1
- package/package/corpus/skills/cratis-screenplay-ui-composition/SKILL.md +2 -0
- package/package/corpus/skills/cratis-screenplay-ui-composition/references/screen-composition-contract.md +27 -9
- package/package/corpus/skills/cratis-security-review/SKILL.md +9 -3
- package/package/corpus/skills/cratis-security-review/references/arc-boundaries.md +59 -0
- package/package/corpus/skills/cratis-specification-by-example/SKILL.md +7 -0
- package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +43 -11
- package/package/corpus/skills/cratis-specifications-csharp/references/application-scenarios.md +16 -4
- package/package/corpus/skills/cratis-specifications-csharp/references/csharp-patterns.md +18 -5
- package/package/corpus/skills/cratis-specifications-csharp/references/integration-specs.md +21 -0
- package/package/corpus/skills/cratis-specifications-csharp/references/shared-lifecycle.md +34 -0
- package/package/corpus/skills/cratis-specifications-csharp/verification.json +13 -0
- package/package/corpus/skills/cratis-specifications-typescript/SKILL.md +14 -5
- package/package/corpus/skills/cratis-specifications-typescript/verification.json +12 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +8 -2
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +6 -3
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/rendered-application.md +10 -1
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/scene-runtime.md +75 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/verification.json +5 -0
- package/package/corpus/skills/cratis-studio-mcp-safety-guidance/SKILL.md +14 -1
- package/package/profile-catalog.json +68 -1
- package/package.json +1 -1
- package/src/index.ts +3 -1
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
2
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
3
|
+
|
|
4
|
+
# Testing without a kernel
|
|
5
|
+
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- Appends — EventScenario
|
|
9
|
+
- Deliveries — ReactorScenario
|
|
10
|
+
- Materialization — ReadModelScenario
|
|
11
|
+
- Use kernel-backed tests outside the boundary
|
|
12
|
+
|
|
13
|
+
Verified at `Cratis/Chronicle.TypeScript` **v6.51.0**:
|
|
14
|
+
`Source/testing/index.ts`, `EventScenario.ts`, `ReactorScenario.ts`,
|
|
15
|
+
`ReadModelScenario.ts`, and `Documentation/testing.md`.
|
|
16
|
+
Import the three scenarios from `@cratis/chronicle/testing`. They share
|
|
17
|
+
production dispatch/definition code within a **fixture-bounded** evaluator;
|
|
18
|
+
a green scenario is not proof of kernel storage, scheduling or compliance.
|
|
19
|
+
|
|
20
|
+
The examples below share these standard-decorator artifacts:
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
24
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
25
|
+
|
|
26
|
+
import 'reflect-metadata';
|
|
27
|
+
import { field } from '@cratis/fundamentals';
|
|
28
|
+
import { eventType, fromEvent, reactor } from '@cratis/chronicle';
|
|
29
|
+
import { EventScenario, ReactorScenario, ReadModelScenario } from '@cratis/chronicle/testing';
|
|
30
|
+
|
|
31
|
+
@eventType()
|
|
32
|
+
class MessageRecorded {
|
|
33
|
+
@field(String) text: string;
|
|
34
|
+
constructor(text: string) { this.text = text; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@fromEvent(MessageRecorded)
|
|
38
|
+
class Message {
|
|
39
|
+
@field(String) id = '';
|
|
40
|
+
@field(String) text = '';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@eventType()
|
|
44
|
+
class MessageCopied {
|
|
45
|
+
@field(String) text: string;
|
|
46
|
+
constructor(text: string) { this.text = text; }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@reactor()
|
|
50
|
+
class CopyMessage {
|
|
51
|
+
messageRecorded(event: MessageRecorded): MessageCopied {
|
|
52
|
+
return new MessageCopied(event.text);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Appends — EventScenario
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
const events = new EventScenario({
|
|
61
|
+
artifacts: { eventTypes: [MessageRecorded] }, constraints: 'disabled'
|
|
62
|
+
});
|
|
63
|
+
await events.given.forEventSource('message-1').events(new MessageRecorded('seed'));
|
|
64
|
+
const result = await events.when.forEventSource('message-2').event(new MessageRecorded('act'));
|
|
65
|
+
if (!result.isSuccess) throw new Error('Expected the append to succeed');
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`given...events` seeds sequential single appends; `when.event` is a single
|
|
69
|
+
append and `when.events` an atomic batch. `forEventSource(id, options?)` accepts
|
|
70
|
+
fixture-backed routing. Omitted routes resolve to `Default`/`All`/`Default`.
|
|
71
|
+
`constraints: 'disabled'` is explicit opt-out, never a silent fallback.
|
|
72
|
+
Selected `@unique`/fluent constraints support fixture-proven key domains,
|
|
73
|
+
removal cycles and scopes; unproven shapes throw.
|
|
74
|
+
|
|
75
|
+
## Deliveries — ReactorScenario
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
const deliveries = new ReactorScenario(CopyMessage, {
|
|
79
|
+
artifacts: { eventTypes: [MessageRecorded, MessageCopied] }, constraints: 'disabled'
|
|
80
|
+
});
|
|
81
|
+
await deliveries.when.forEventSource('message-3').events(new MessageRecorded('copy'));
|
|
82
|
+
deliveries.shouldHaveProduced(MessageCopied, event => event.text === 'copy');
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Returned events are **recorded, not appended or recursively delivered**. A
|
|
86
|
+
production reactor returning its own subscribed type may loop; the scenario
|
|
87
|
+
does not test termination. `produced`, `sideEffects` and `results`
|
|
88
|
+
expose observations. Failed deliveries reject the call and prevent subsequent
|
|
89
|
+
deliveries; retries/checkpoints are not simulated. Content is client-serialized
|
|
90
|
+
JSON, not necessarily kernel-normalized content. Explicit non-subscribed
|
|
91
|
+
service appends enter history but are not delivered to observers.
|
|
92
|
+
|
|
93
|
+
## Materialization — ReadModelScenario
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
const models = new ReadModelScenario(Message);
|
|
97
|
+
models.given.forEventSource('message-4').events(new MessageRecorded('read'));
|
|
98
|
+
const message = await models.instanceForEventSourceId('message-4');
|
|
99
|
+
if (message?.text !== 'read') throw new Error('Expected the materialized message');
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`instance` requires a sole model; `instanceForEventSourceId` returns null when
|
|
103
|
+
absent, and `wasDeletedForEventSourceId` distinguishes deletion. For composition,
|
|
104
|
+
use `new ReadModelScenario(Message).observe(events)` (or a ReactorScenario).
|
|
105
|
+
Every read replays the source's accepted history; returned reactor effects do
|
|
106
|
+
not reach it. Do not mix observing and seeding or observe twice.
|
|
107
|
+
|
|
108
|
+
Reducers and fixture-backed projections work: scalar root mappings/AutoMap,
|
|
109
|
+
removal/recreation, bounded root arithmetic, one level of typed string-keyed
|
|
110
|
+
children, and one string-keyed root join. The **whole** definition is validated
|
|
111
|
+
before replay, even for events you never seed.
|
|
112
|
+
|
|
113
|
+
## Use kernel-backed tests outside the boundary
|
|
114
|
+
|
|
115
|
+
`UnsupportedEventSequenceOperation`, `UnsupportedReactorOperation`, and
|
|
116
|
+
`UnsupportedProjectionOperation` name the operation/artifact and require a
|
|
117
|
+
kernel-backed test. Do not catch them and call the scenario green.
|
|
118
|
+
|
|
119
|
+
Unsupported examples include non-fixture-backed constraints, concurrency,
|
|
120
|
+
transactions, compliance, named tags on input appends, tag-filtered reactors,
|
|
121
|
+
replay/redelivery, nested children, child/nested joins, `removedWithJoin`,
|
|
122
|
+
variants and custom/composite keys. Named tags on returned reactor effects can
|
|
123
|
+
be recorded, but are not proof of persisted named-tag behavior. Nondefault
|
|
124
|
+
routes cannot be evaluated by ReadModelScenario. Read the tagged
|
|
125
|
+
`Documentation/testing.md` capability tables before claiming an advanced shape
|
|
126
|
+
is covered; use a live kernel for storage and observer fidelity.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
## Variants — mutually exclusive read models for one entity's lifecycle
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
5
|
-
>
|
|
3
|
+
> Available since `@cratis/chronicle` 6.2.0; verified at **v6.51.0**
|
|
4
|
+
> (`Source/projections/VariantReclassifier.ts` and model-bound decorators).
|
|
5
|
+
> Read models are inferred; use `static readModelId` for a custom id.
|
|
6
6
|
|
|
7
7
|
Some entities do not have one shape for their whole lifetime — a work item is a
|
|
8
8
|
backlog entry until a pull request exists for it, then it is a pull request
|
|
@@ -16,7 +16,6 @@ class WorkItem {} // anchors the group; not itself a read model
|
|
|
16
16
|
@variantOf(WorkItem, 'id')
|
|
17
17
|
@entersOn(IssueCreated)
|
|
18
18
|
@fromEvent(IssueCreated)
|
|
19
|
-
@readModel()
|
|
20
19
|
class BacklogItem {
|
|
21
20
|
id = '';
|
|
22
21
|
@setFrom(IssueCreated, 'title') title = '';
|
|
@@ -26,7 +25,6 @@ class BacklogItem {
|
|
|
26
25
|
@entersOn(PullRequestCreated)
|
|
27
26
|
@fromEvent(PullRequestCreated)
|
|
28
27
|
@fromEvent(BuildCompleted) // not the entering event -> update-only join
|
|
29
|
-
@readModel()
|
|
30
28
|
class PullRequestItem {
|
|
31
29
|
id = '';
|
|
32
30
|
@setFrom(PullRequestCreated, 'pullRequestUrl') pullRequestUrl = '';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "cratis-chronicle-client-typescript",
|
|
3
|
+
"input": "Append a routed event and specify a TypeScript Chronicle reactor without a kernel.",
|
|
4
|
+
"fixtures": [],
|
|
5
|
+
"assertions": [
|
|
6
|
+
{ "kind": "skill-contains", "value": "tag **`v6.51.0`**" },
|
|
7
|
+
{ "kind": "skill-contains", "value": "**Single `append()` supports routing and subject options.**" },
|
|
8
|
+
{ "kind": "skill-contains", "value": "Standard TC39 decorators" },
|
|
9
|
+
{ "kind": "skill-contains", "value": "**A compiled-JS entry defaults to `[]`.**" },
|
|
10
|
+
{ "kind": "skill-contains", "value": "IncompatibleChronicleServer" },
|
|
11
|
+
{ "kind": "skill-contains", "value": "installed package version" },
|
|
12
|
+
{ "kind": "skill-contains", "value": "**Reactors replay by default since 6.9.0.**" },
|
|
13
|
+
{ "kind": "skill-contains", "value": "@handles(EventType)" },
|
|
14
|
+
{ "kind": "skill-contains", "value": "Returns **null when absent**", "file": "references/read-models-and-projections.md" },
|
|
15
|
+
{ "kind": "skill-contains", "value": "`@unique()`", "file": "references/read-models-and-projections.md" },
|
|
16
|
+
{ "kind": "skill-contains", "value": "**recorded, not appended or recursively delivered**", "file": "references/testing.md" },
|
|
17
|
+
{ "kind": "skill-contains", "value": "UnsupportedProjectionOperation", "file": "references/testing.md" }
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -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` | `
|
|
21
|
-
| `Cratis.Chronicle.CodeAnalysis` | `
|
|
20
|
+
| `Cratis.Chronicle` | `19.50.1` | `Cratis.Chronicle.Compliance.GDPR`, `Cratis.Chronicle.SubjectAttribute`, `IEventSequence.Redact`, `IReadModels.Release` |
|
|
21
|
+
| `Cratis.Chronicle.CodeAnalysis` | `19.50.1` | `CHR0026`, `CHR0034`, `CHR0035`, `CHR0043`, `CHR0046` |
|
|
22
22
|
|
|
23
23
|
Reverify product sources before claiming support for another version.
|
|
24
24
|
|
|
@@ -58,10 +58,10 @@ it.** The rationale that actually reaches the event schema comes from
|
|
|
58
58
|
marker and `[ComplianceDetails("...")]` for the reason; do not write
|
|
59
59
|
`[PII("reason")]` and expect the reason to be recorded.
|
|
60
60
|
|
|
61
|
-
`PII` is the only compliance category Chronicle defines
|
|
62
|
-
|
|
63
|
-
`[
|
|
64
|
-
|
|
61
|
+
`PII` is the only compliance category Chronicle defines. The protection markers
|
|
62
|
+
are `[PII]` for personal data and `[Encrypted]` for retained operational secrets;
|
|
63
|
+
there is no `[Redact]`, `[Erase]`, `[Anonymize]`, or `[Encrypt]` annotation.
|
|
64
|
+
Redaction is an API call. `[NotAudited]` belongs to Arc, not Chronicle.
|
|
65
65
|
|
|
66
66
|
`[Encrypted]` (`Cratis.Chronicle.ProtectedValues`, Chronicle 19.32.0+) is a
|
|
67
67
|
security marker, not a compliance category: it encrypts an operational secret
|
|
@@ -70,6 +70,11 @@ and no erasure. Never put it on a value that is also `[PII]` (analyzer
|
|
|
70
70
|
**CHR0053**, which `[PII]` alone already covers), nor on an event source id
|
|
71
71
|
(**CHR0052**). Arc's `[NotAudited]` is not a Chronicle attribute either; a secret
|
|
72
72
|
that is also a command property takes both `[Encrypted]` and `[NotAudited]`.
|
|
73
|
+
`EncryptedAttribute.Scope` supports `EncryptionScope.Subject` (default),
|
|
74
|
+
`Namespace`, and `Global`; these are security-key scopes, not PII erasure.
|
|
75
|
+
`PIIManager` refuses to delete security keys. Custom security metadata providers
|
|
76
|
+
live in `Cratis.Chronicle.Confidentiality` (`ICanProvideSecurityMetadataForType`
|
|
77
|
+
and `ICanProvideSecurityMetadataForProperty`).
|
|
73
78
|
|
|
74
79
|
Mark only genuinely personal values. Business metadata, identifiers of
|
|
75
80
|
non-persons, and amounts stay unmarked — every marked value costs an encryption
|
|
@@ -152,9 +157,11 @@ What this does and does not do:
|
|
|
152
157
|
recorded before any key is deleted.
|
|
153
158
|
- The events stay in the log. Their `[PII]` values remain as unreadable
|
|
154
159
|
ciphertext — this is crypto-shredding, not deletion.
|
|
155
|
-
-
|
|
156
|
-
|
|
157
|
-
|
|
160
|
+
- Released values depend on the schema: string → empty string; non-nullable
|
|
161
|
+
number/boolean → `0`/`false`; nullable number/boolean → `null`; collection →
|
|
162
|
+
empty collection; object/value object → empty object. An erased `0`, `false`
|
|
163
|
+
or empty string is indistinguishable from a genuinely recorded one.
|
|
164
|
+
Source at v19.50.0: `Documentation/compliance/erasing-a-subject.mdx`.
|
|
158
165
|
- Appending a new `[PII]` value for an erased subject **fails** with
|
|
159
166
|
`EncryptionKeyErased`. It does not quietly mint a new key. Call
|
|
160
167
|
`AllowNewEncryptionKeyFor` deliberately if the subject must be re-enrolled.
|
|
@@ -162,9 +169,10 @@ What this does and does not do:
|
|
|
162
169
|
carries the individual failures. Treat it as unfinished and retry — the fence
|
|
163
170
|
is already in place, so retrying is safe.
|
|
164
171
|
|
|
165
|
-
**Erasure
|
|
166
|
-
|
|
167
|
-
|
|
172
|
+
**Erasure covers every event store in one namespace**, not just the store from
|
|
173
|
+
which `PII` was resolved: forwarding copies subject keys within that namespace.
|
|
174
|
+
There is no cross-namespace erasure. A request spanning tenants is one call per
|
|
175
|
+
tenant namespace; completing only some is an incomplete erasure.
|
|
168
176
|
|
|
169
177
|
## Redact a stored event
|
|
170
178
|
|
|
@@ -184,6 +192,21 @@ Use redaction for content that should never have been appended. Use key erasure
|
|
|
184
192
|
for a subject exercising a right to erasure. They are different mechanisms and
|
|
185
193
|
one does not imply the other.
|
|
186
194
|
|
|
195
|
+
Since 19.11.0, redaction also drops retained causation property values, revision
|
|
196
|
+
history and content hashes. It does not reach backups, oplogs or events already
|
|
197
|
+
forwarded elsewhere. Subject erasure does **not** erase causation values: these
|
|
198
|
+
are not encrypted under the subject key. Kernel configuration
|
|
199
|
+
`events.causationPropertyRetention = Omit`
|
|
200
|
+
(`Cratis__Chronicle__Events__CausationPropertyRetention`) prevents **new** retention
|
|
201
|
+
on appends, forwarding, revisions and redactions; it does not rewrite history.
|
|
202
|
+
Source at v19.50.0: `Documentation/hosting/configuration/events.md` and
|
|
203
|
+
`Documentation/compliance/erasing-a-subject.mdx`.
|
|
204
|
+
|
|
205
|
+
Adding `[PII]` / `[Encrypted]` to an existing generation is accepted with a warning
|
|
206
|
+
and protects **later events only**. Revision and migration fail rather than
|
|
207
|
+
persist empty substitutes when key lookup/decryption fails; an erased subject
|
|
208
|
+
migrates as erased. Do not confuse that fail-closed write path with read release.
|
|
209
|
+
|
|
187
210
|
## Read personal data back
|
|
188
211
|
|
|
189
212
|
The kernel decrypts on the paths it owns, so projections and observers see plain
|
|
@@ -211,7 +234,7 @@ compliance setup — `AddCompliance()` is already called for you, and the raw
|
|
|
211
234
|
client path discovers the metadata providers by convention. The developer-facing
|
|
212
235
|
requirement is only to annotate the types.
|
|
213
236
|
|
|
214
|
-
Key storage is a hosting decision, not an application one. Chronicle ships
|
|
237
|
+
Key storage is per namespace and is a hosting decision, not an application one. Chronicle ships
|
|
215
238
|
MongoDB (the default), SQL, in-memory, HashiCorp Vault, and Azure Key Vault key
|
|
216
239
|
storage, configured under `Cratis:Chronicle:Compliance:Encryption`.
|
|
217
240
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cratis-chronicle-decision-reads
|
|
3
|
+
description: Guard a state-dependent Chronicle decision with IDecisionReads, projection admission, ambient unit-of-work enrollment and commit conflict handling. Use when an append must remain valid for the facts read, when choosing whole-source versus stream-scoped reads, or diagnosing DecisionReadRefused and scope conflicts. Do not use for ordinary query reads, append-time uniqueness alone, or defining an Arc command's ProtectedDecision API.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
8
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
9
|
+
|
|
10
|
+
# Guard the facts a decision used
|
|
11
|
+
|
|
12
|
+
A fresh read is not enough: another writer can change its facts between reading
|
|
13
|
+
and appending. A decision read folds an admitted projection from events and
|
|
14
|
+
carries a guard to commit. The append is rejected when a later event inside that
|
|
15
|
+
guard could change the decision. This is the Chronicle side of a dynamic
|
|
16
|
+
consistency boundary (DCB), not authorization or a replacement for unique constraints.
|
|
17
|
+
|
|
18
|
+
## Verified product sources
|
|
19
|
+
|
|
20
|
+
| Product | Version | Sources |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| `Cratis.Chronicle` | `19.50.1` | `Source/Clients/DotNET/ReadModels/{IDecisionReads,DecisionReads,DecisionRead,DecisionReadRefusalReason}.cs` |
|
|
23
|
+
| `Cratis.Chronicle` transactions | `19.50.1` | `Source/Clients/DotNET/Transactions/{IUnitOfWork,UnitOfWork,DecisionReadCommitOwner,UnitOfWorkLifecyclePolicy}.cs` |
|
|
24
|
+
| `Cratis.Chronicle` scope resolution | `19.50.1` | `Source/Clients/DotNET/EventSequences/Concurrency/DefinitionConcurrencyScopeResolver.cs` |
|
|
25
|
+
|
|
26
|
+
The client surface began in 19.9.0; stream-scoped reads require the newer kernel
|
|
27
|
+
surface added in 19.39.0. Reverify a client/kernel pair rather than disabling
|
|
28
|
+
compatibility checking to force a read through.
|
|
29
|
+
|
|
30
|
+
## Route the work
|
|
31
|
+
|
|
32
|
+
- For Arc `[ProtectedDecision]`, Arc command/handler shapes and Arc `IStreamReads`,
|
|
33
|
+
use **cratis-arc-command** and **cratis-arc-command-validation**; do not recreate
|
|
34
|
+
their unit-of-work ownership in a handler. The Chronicle `DecisionRead<T>`
|
|
35
|
+
below is not a license to invent an Arc wrapper shape.
|
|
36
|
+
- For a unique value claimed atomically or a closed stream, use
|
|
37
|
+
**cratis-chronicle-event-constraints**.
|
|
38
|
+
- For read-model declarations, use **cratis-chronicle-read-model** and
|
|
39
|
+
**cratis-chronicle-projection**. Ordinary `IReadModels` reads do not enroll guards.
|
|
40
|
+
- In model-first scope, settle the rule and its refusal/concurrency specifications
|
|
41
|
+
in the accepted model using **cratis-screenplay-event-modeling** before gap-fill.
|
|
42
|
+
Never broaden a projection or bypass model protection just to make admission pass.
|
|
43
|
+
|
|
44
|
+
## Check admission, then read
|
|
45
|
+
|
|
46
|
+
`IDecisionReads` is in `Cratis.Chronicle.ReadModels`; obtain the reader with
|
|
47
|
+
`eventStore.GetDecisionReads()` (`ReadModels/DecisionReadExtensions.cs`).
|
|
48
|
+
`Admit<T>()` / `AdmitForStream<T>()` check shape **without I/O**, returning
|
|
49
|
+
`DecisionReadAdmission(IsAdmitted, Reason)`. A successful shape check is not
|
|
50
|
+
runtime read success: definitions, key conversion and fold completion are also
|
|
51
|
+
checked while reading.
|
|
52
|
+
|
|
53
|
+
`Get<T>(ReadModelKey key, cancellationToken)` requires an ambient unit of work
|
|
54
|
+
and enrolls the protected read into it. Without one it throws
|
|
55
|
+
`DecisionReadRequiresUnitOfWork`. `GetDetached<T>` reads without enrollment:
|
|
56
|
+
explicitly add it with `IUnitOfWork.AddDecisionRead(read)` or carry it to a guarded
|
|
57
|
+
append; dropping its guard makes the decision unprotected.
|
|
58
|
+
|
|
59
|
+
`DecisionRead<T>` exposes `Instance` (nullable), `Exists`, `Key`, `IsProtected`,
|
|
60
|
+
and store/namespace/sequence/stream routing. An absent instance is still a fact
|
|
61
|
+
to guard, not permission to return a fabricated default model. Decide the
|
|
62
|
+
business rejection or creation behavior explicitly.
|
|
63
|
+
|
|
64
|
+
Read [admission and ownership](references/admission-and-ownership.md) when a
|
|
65
|
+
shape is refused, a read crosses a stream boundary, or nested work tries to
|
|
66
|
+
complete a protected transaction.
|
|
67
|
+
|
|
68
|
+
## Read one stream instead of the whole source
|
|
69
|
+
|
|
70
|
+
The stream overload takes `eventSourceId`, `eventStreamType`, `eventStreamId`,
|
|
71
|
+
optional `eventSourceType`, and cancellation; it has both `Get<T>` and
|
|
72
|
+
`GetDetached<T>` forms. It folds only that stream and guards it independently of
|
|
73
|
+
the model's key. It still admits **projections only**, not reducers or arbitrary
|
|
74
|
+
queries. Select one stream type, not `EventStreamType.All`; a stream-keyed
|
|
75
|
+
projection also needs an explicit stream id, not the default wildcard.
|
|
76
|
+
`AdmitForStream<T>()` checks this shape; unsupported readers/kernels refuse with
|
|
77
|
+
`StreamScopeNotSupported`. Do not fall back to an unguarded query on refusal.
|
|
78
|
+
|
|
79
|
+
## Commit and inspect conflicts
|
|
80
|
+
|
|
81
|
+
Enroll the read **before** staging its decision's events through
|
|
82
|
+
`EventLog.Transactional` and let the owning unit of work commit. Inspect
|
|
83
|
+
`IsSuccess`, append errors/constraint violations/concurrency violations and
|
|
84
|
+
`GetDecisionConflicts()`; the latter reports decision conflicts without exposing
|
|
85
|
+
sequence numbers. A checked empty append validates its scope without writing
|
|
86
|
+
an event. Do not mistake "no event" for "no consistency check".
|
|
87
|
+
|
|
88
|
+
Do not attach a separate explicit concurrency scope to a source already guarded
|
|
89
|
+
by a decision read: `DecisionReadConflictsWithConcurrencyScope` rejects that
|
|
90
|
+
combination. A batch needing incompatible scopes for one event source throws
|
|
91
|
+
`IncompatibleConcurrencyScopesForEventSource`. These are configuration/enrollment
|
|
92
|
+
errors, not an ordinary business rejection. Choose one coherent scope; do not
|
|
93
|
+
catch them and append without protection.
|
|
94
|
+
|
|
95
|
+
On a real conflict, reread and **re-decide** with the same authorization and
|
|
96
|
+
business rule; never resend stale events as if a retry fixed the decision. A
|
|
97
|
+
DCB guard protects event facts, not external side effects. Durable follow-up
|
|
98
|
+
belongs in a reactor/outbox; use **cratis-chronicle-reactor**.
|
|
99
|
+
|
|
100
|
+
## Verify
|
|
101
|
+
|
|
102
|
+
- The model is admitted and the read is protected and enrolled in the intended
|
|
103
|
+
store, namespace and stream before staging events.
|
|
104
|
+
- Refusal is surfaced, never replaced by a materialized/unprotected read.
|
|
105
|
+
- One owner completes or rolls back; nested code does neither behind it.
|
|
106
|
+
- Competing relevant events cause rejection; unrelated events outside the guard
|
|
107
|
+
do not. Absence, empty checked append and stream isolation have specifications.
|
|
108
|
+
- Scope conflicts, lifecycle violations and failed commit remain visible.
|
|
109
|
+
- Real-client/kernel integration proves the concurrency boundary; a default
|
|
110
|
+
`EventScenario` scope strategy returning `None` is not concurrency evidence.
|
package/package/corpus/skills/cratis-chronicle-decision-reads/references/admission-and-ownership.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<!-- Copyright (c) Cratis. All rights reserved. -->
|
|
2
|
+
<!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
|
|
3
|
+
|
|
4
|
+
# Admission and commit ownership
|
|
5
|
+
|
|
6
|
+
Verified at **Cratis/Chronicle v19.50.0**:
|
|
7
|
+
`Source/Clients/DotNET/ReadModels/DecisionReads.cs`, `DecisionReadRefusalReason.cs`,
|
|
8
|
+
`Source/Clients/DotNET/Transactions/UnitOfWork.cs`, `DecisionReadCommitOwner.cs`,
|
|
9
|
+
and `UnitOfWorkLifecyclePolicy.cs`.
|
|
10
|
+
|
|
11
|
+
## Why a shape is refused
|
|
12
|
+
|
|
13
|
+
Admission requires exactly one supported projection on the event log. It is not
|
|
14
|
+
an adapter for arbitrary read-model storage:
|
|
15
|
+
|
|
16
|
+
| Reason | Shape to inspect |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| Reducer / AmbiguousProjection / NotEventLog | reduced model, several projections, or another sequence |
|
|
19
|
+
| Join / Hierarchy | joins, children or nested projections |
|
|
20
|
+
| OpenEndedEventTypes / Derivatives / FromEventProperty | all-events, FromEvery, or event-property routing |
|
|
21
|
+
| NotEventSourceKeyed / KeyConversion | keys not convertible to the admitted raw source key |
|
|
22
|
+
| NoEventTypes / UnsupportedEventTypeId | no finite guarded type set or unsupported encoded type ids |
|
|
23
|
+
| InvalidKey / FoldIncomplete / DefinitionMismatch | runtime key, fold or client/kernel definition disagreement |
|
|
24
|
+
| StreamKeyRequiresStreamScope | stream-keyed model read without a stream scope |
|
|
25
|
+
| StreamScopeNotSupported / AllStreamsNotSupported / StreamKeyRequiresExplicitStreamId | unsupported stream reader or incomplete stream selection |
|
|
26
|
+
|
|
27
|
+
Whole-source key admission supports string/Guid schemas, with canonical lowercase
|
|
28
|
+
D-form Guid source ids assumed. A renamed or custom-converted key is not made
|
|
29
|
+
safe by a cast in the caller. Runtime definition comparison is a diagnostic, not
|
|
30
|
+
proof of equality with an executing projection. Keep integration specifications
|
|
31
|
+
for the actual kernel pair and model shape.
|
|
32
|
+
|
|
33
|
+
Do not silently replace a refused decision read with a materialized sink read.
|
|
34
|
+
Redesign the decision model as a focused admitted projection only when the
|
|
35
|
+
contract permits it, or surface the missing capability.
|
|
36
|
+
|
|
37
|
+
## Completion is owned
|
|
38
|
+
|
|
39
|
+
`IUnitOfWork.HasEnrolledDecisionReads` is sticky: it remains true after commit,
|
|
40
|
+
rollback or disposal. It does not mean the transaction is still open.
|
|
41
|
+
`UnitOfWork.ClaimDecisionReadCommitOwnership()` returns the opaque
|
|
42
|
+
`DecisionReadCommitOwner` used by `CommitAsOwner` and `RollbackAsOwner`; these are concrete ownership capabilities, not methods on
|
|
43
|
+
`IUnitOfWork`. Once ownership is claimed, nested work must not complete that
|
|
44
|
+
protected transaction using ordinary commit/rollback. Arc's ownership wrapper
|
|
45
|
+
belongs in **cratis-arc-command** / **cratis-arc-command-validation**, not custom
|
|
46
|
+
application plumbing.
|
|
47
|
+
|
|
48
|
+
`ChronicleOptions.UnitOfWorkLifecyclePolicy` defaults to `Compatibility` for major
|
|
49
|
+
19; `Strict` opts in and is planned as the next-major default. Compatibility
|
|
50
|
+
retains legacy late staging only for unprotected work and logs it. Strict rejects
|
|
51
|
+
all late staging. Protected work and owner completion checks remain enforced;
|
|
52
|
+
`UnitOfWorkIsCompleting` must not be caught and interpreted as success. An
|
|
53
|
+
in-flight commit cannot be undone by a competing rollback.
|
|
54
|
+
|
|
55
|
+
Keep read, decision, enrollment and completion inside one ambient async context.
|
|
56
|
+
If a commit rejects the read, start a new read/decision attempt rather than
|
|
57
|
+
reusing the stale protected instance or appending its events without the guard.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill": "cratis-chronicle-decision-reads",
|
|
3
|
+
"input": "Guard a standalone Chronicle decision through its owning commit.",
|
|
4
|
+
"fixtures": [],
|
|
5
|
+
"assertions": [
|
|
6
|
+
{ "kind": "skill-contains", "value": "DecisionReadRequiresUnitOfWork" },
|
|
7
|
+
{ "kind": "skill-contains", "value": "AdmitForStream<T>()" },
|
|
8
|
+
{ "kind": "skill-contains", "value": "DecisionReadConflictsWithConcurrencyScope" },
|
|
9
|
+
{ "kind": "skill-contains", "value": "GetDecisionConflicts()" },
|
|
10
|
+
{ "kind": "skill-contains", "value": "reread and **re-decide**" }
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -17,11 +17,12 @@ Constraints are discovered automatically. There is no registration call.
|
|
|
17
17
|
|
|
18
18
|
| Package | Version | Purpose |
|
|
19
19
|
| --- | --- | --- |
|
|
20
|
-
| `Cratis.Chronicle` (client) | `
|
|
21
|
-
| `Cratis.Chronicle` code analysis | `
|
|
22
|
-
| `Cratis.Chronicle.Testing` | `
|
|
20
|
+
| `Cratis.Chronicle` (client) | `19.50.1` | uniqueness, stream closing, constraint builders and append results |
|
|
21
|
+
| `Cratis.Chronicle` code analysis | `19.50.1` | `CHR0017`, `CHR0018`, `CHR0020` |
|
|
22
|
+
| `Cratis.Chronicle.Testing` | `19.50.1` | `EventScenario` and the `IAppendResult` `Should*` assertions |
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Sources: `Source/Clients/DotNET/Events/Constraints/`,
|
|
25
|
+
`EventSequences/IEventSequence.cs` and `Source/Clients/Testing/` at v19.50.0.
|
|
25
26
|
|
|
26
27
|
Reverify against the Chronicle repository before claiming support for another
|
|
27
28
|
version. Never translate an attribute argument order or an assertion name from
|
|
@@ -31,8 +32,9 @@ memory.
|
|
|
31
32
|
|
|
32
33
|
- The rule rejects **command input** — a range, a format, a cross-field
|
|
33
34
|
condition: use `cratis-arc-command-validation`.
|
|
34
|
-
- The rule is state-dependent and
|
|
35
|
-
|
|
35
|
+
- The rule is state-dependent and needs concurrency protection: use enrolled
|
|
36
|
+
stream/projection decision reads with `cratis-arc-command-validation`.
|
|
37
|
+
Plain injected models and `Result<,>` alone remain advisory.
|
|
36
38
|
- You are creating or changing a read model that the rule reads: use
|
|
37
39
|
`cratis-chronicle-read-model`.
|
|
38
40
|
- You are writing the specification and want the `EventScenario` mechanics in
|
|
@@ -58,12 +60,12 @@ memory.
|
|
|
58
60
|
| The same value must be unique across **several** event types | `IConstraint` with one `[Unique(name: …)]` name, or a fluent `Unique(…)` |
|
|
59
61
|
| Case-insensitive matching, composite keys, or a scoped constraint | `IConstraint` |
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
Class-level `[Unique]` (and `Unique<TEvent>()`) means "at most one such event
|
|
64
|
+
per event source id". Property-level `[Unique]` (and `Unique(unique => unique.On<T>(…))`)
|
|
65
|
+
means "this value is unique across event sources". `Mode` tunes **property-value
|
|
66
|
+
retention**, not that distinction: `PerEventSource` (default) releases the old
|
|
67
|
+
value when the source claims a new one; `PerValue` keeps every value reserved
|
|
68
|
+
until a removal releases it. Reclaiming one's own value succeeds in either mode.
|
|
67
69
|
|
|
68
70
|
## Step 2 — `[Unique]` — the default
|
|
69
71
|
|
|
@@ -94,8 +96,16 @@ public record UserRemoved;
|
|
|
94
96
|
several on one removal event to release several claimed values at once.
|
|
95
97
|
- To enforce the same property across **multiple** event types, give each its
|
|
96
98
|
`[Unique(name: "UniqueEmail")]` with the **same name**.
|
|
97
|
-
- Several removal events may declare the same name; **any one
|
|
98
|
-
|
|
99
|
+
- Several removal events may declare the same name; **any one releases the claim**.
|
|
100
|
+
- `Mode = UniqueConstraintMode.PerValue` retains all values. A removal can use
|
|
101
|
+
`Properties = [nameof(RemovalEvent.Value)]` to release one value (composite
|
|
102
|
+
properties in constraint order); empty `Properties` releases all values held
|
|
103
|
+
by the source in the scope.
|
|
104
|
+
- `EventSequences = [EventSequenceId.LogId]` limits validation/indexing to the
|
|
105
|
+
event log. Empty means every sequence; when declarations share a name, their
|
|
106
|
+
sequence sets combine and an unrestricted declaration wins.
|
|
107
|
+
- Null/missing property values do not claim `""`; clearing a default-mode value
|
|
108
|
+
releases its claim. `IgnoreCasing` is honored by the kernel.
|
|
99
109
|
|
|
100
110
|
## Step 3 — `IConstraint` — the advanced form
|
|
101
111
|
|
|
@@ -116,7 +126,7 @@ public class UniqueProjectName : IConstraint
|
|
|
116
126
|
```
|
|
117
127
|
|
|
118
128
|
`IUniqueConstraintBuilder` also carries `WithName(name)`, `WithMessage(message)`,
|
|
119
|
-
and an `On(EventType, string[])` overload. `On<TEvent>` is **variadic** —
|
|
129
|
+
`WithMode(UniqueConstraintMode)`, and an `On(EventType, string[])` overload. `On<TEvent>` is **variadic** —
|
|
120
130
|
`On<T>(e => e.Tenant, e => e.Email)` constrains the composite of both
|
|
121
131
|
properties, not two independent values.
|
|
122
132
|
|
|
@@ -130,6 +140,8 @@ Always pass them by name.
|
|
|
130
140
|
|
|
131
141
|
`ConstraintScope` is a record of three optional dimensions, not an enum, and you
|
|
132
142
|
never construct it — the three `Per…` calls select which dimensions participate.
|
|
143
|
+
`ForEventSequences(...)` / `ForEventLog()` select the sequences to validate and
|
|
144
|
+
index. Keep these separate from scope dimensions.
|
|
133
145
|
|
|
134
146
|
## Step 4 — Keep `Define` declarative
|
|
135
147
|
|
|
@@ -212,10 +224,37 @@ Use per-test unique values (`$"{Guid.NewGuid():N}"`) so specifications are not
|
|
|
212
224
|
order-dependent. For a release, append the removal event and then assert the
|
|
213
225
|
value can be claimed again.
|
|
214
226
|
|
|
215
|
-
⚠️
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
227
|
+
⚠️ **The default `EventScenario` client scope strategy answers `ConcurrencyScope.None`.**
|
|
228
|
+
A scenario-only concurrency assertion without a checked scope is vacuous.
|
|
229
|
+
The scenario runs the kernel grain/validator, but explicit-scope violations
|
|
230
|
+
have not been proven end to end here; use real-client integration for that proof.
|
|
231
|
+
Constraints are fully exercisable. Dispose the scenario in `Destroy()`.
|
|
232
|
+
|
|
233
|
+
## Close a stream at its business end
|
|
234
|
+
|
|
235
|
+
Choose the lifetime with **cratis-chronicle-event-modeling**, then enforce it at
|
|
236
|
+
append time. `IEventSequence.CompleteStream(ClosedStreamScope,
|
|
237
|
+
expectedTailSequenceNumber?)` returns `Result<EventSequenceNumber, CompleteStreamError>`:
|
|
238
|
+
`EmptyScope`, `AlreadyCompleted`, or `ExpectedTailMismatch` are not successful
|
|
239
|
+
new closures. An append matching a persisted closed scope is refused with
|
|
240
|
+
`StreamClosed`. Unspecified dimensions are wildcards, so name the intended
|
|
241
|
+
source/type/stream dimensions precisely.
|
|
242
|
+
|
|
243
|
+
Prefer an event-owned declaration when closing is a business fact:
|
|
244
|
+
`[ClosesStream]` / `IConstraintBuilder.ClosesStreamOn<TEvent>()`. The attribute
|
|
245
|
+
has `Dimensions`, `EventStreamIdFrom`, `ReopenedBy`, `Name` and `EventSequences`;
|
|
246
|
+
default dimensions are source id, stream type and stream id. Closing happens in
|
|
247
|
+
the **same grain turn** as append, **not crash-atomically** with it. Registering
|
|
248
|
+
the declaration is not retroactive; reindex rebuilds event-owned closures.
|
|
249
|
+
Reopening events remove only their constraint's exact closure, never a manual
|
|
250
|
+
operator closure.
|
|
251
|
+
|
|
252
|
+
Read [closing scope and recovery](references/closing-streams.md) when implementing
|
|
253
|
+
"close the books", choosing a scope, specifying the refusal, or repairing a
|
|
254
|
+
mistaken manual close. `IsStreamCompleted` and `GetClosedStreams` inspect;
|
|
255
|
+
Workbench's **Closed streams** page is read-only. Kernel `ReopenStreamScope`
|
|
256
|
+
requires an authorized operator and records `StreamScopeReopened` before
|
|
257
|
+
removing a manual closure. Closing does not erase history or prevent replay.
|
|
219
258
|
|
|
220
259
|
## What breaks
|
|
221
260
|
|