@cratis/pi 7.61.0 → 7.63.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/spec-writer.md +2 -2
- package/package/corpus/harnesses/copilot/instructions/application-profile.instructions.md +2 -2
- package/package/corpus/harnesses/copilot/instructions/glossary.instructions.md +2 -2
- package/package/corpus/harnesses/copilot/instructions/specs.scenarios.csharp.instructions.md +7 -4
- package/package/corpus/harnesses/copilot/instructions/vertical-slices.instructions.md +22 -13
- package/package/corpus/product-sync.json +11 -10
- package/package/corpus/prompts/add-business-rule.prompt.md +1 -1
- package/package/corpus/rules/application-profile.md +2 -2
- package/package/corpus/rules/glossary.md +2 -2
- package/package/corpus/rules/specs.scenarios.csharp.md +7 -4
- package/package/corpus/rules/vertical-slices.md +22 -13
- package/package/corpus/skills/cratis-application-slice-conformance/SKILL.md +1 -1
- package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +4 -3
- package/package/corpus/skills/cratis-application-slice-specifications/references/query-and-decision-scenarios.md +12 -3
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +12 -7
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +11 -11
- package/package/corpus/skills/cratis-arc-command/SKILL.md +3 -2
- package/package/corpus/skills/cratis-arc-command/references/decisions-and-routing.md +95 -51
- package/package/corpus/skills/cratis-arc-command/references/fresh-identifiers.md +5 -2
- package/package/corpus/skills/cratis-arc-command/references/generated-values.md +27 -12
- package/package/corpus/skills/cratis-arc-command/references/read-model-injection.md +9 -5
- package/package/corpus/skills/cratis-arc-command/references/shop-generation.md +17 -6
- package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +99 -52
- package/package/corpus/skills/cratis-chronicle-decision-reads/SKILL.md +37 -15
- package/package/corpus/skills/cratis-chronicle-decision-reads/references/admission-and-ownership.md +3 -2
- package/package/corpus/skills/cratis-chronicle-decision-reads/references/choosing-how-a-rule-holds.md +277 -138
- package/package/corpus/skills/cratis-chronicle-event-constraints/SKILL.md +4 -4
- package/package/corpus/skills/cratis-chronicle-event-modeling/SKILL.md +6 -4
- package/package/corpus/skills/cratis-chronicle-read-model/SKILL.md +2 -1
- package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +6 -3
- package/package/corpus/skills/cratis-code-review/SKILL.md +6 -3
- package/package/corpus/skills/cratis-fundamentals-concept/SKILL.md +10 -2
- package/package/corpus/skills/cratis-fundamentals-concept/references/shop-generation.md +49 -6
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/SKILL.md +43 -41
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/cases-to-specify.md +11 -2
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/integration-interview.md +24 -0
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/public-events-and-direction.md +76 -10
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/realization-and-gap-fill.md +5 -0
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/todo-list-example.md +1 -1
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/translate-example.md +39 -23
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/translation-patterns.md +35 -12
- package/package/corpus/skills/cratis-screenplay-captures-and-reactions/SKILL.md +51 -81
- package/package/corpus/skills/cratis-screenplay-captures-and-reactions/references/atomic-batches-and-source-mappings.md +83 -0
- package/package/corpus/skills/cratis-screenplay-captures-and-reactions/references/capture-map-and-example.md +14 -4
- package/package/corpus/skills/cratis-screenplay-captures-and-reactions/references/reaction-forms.md +59 -0
- package/package/corpus/skills/cratis-screenplay-captures-and-reactions/references/record-capture-specifications.md +60 -0
- package/package/corpus/skills/cratis-screenplay-captures-and-reactions/references/specifying-triggers-and-clock.md +5 -1
- package/package/corpus/skills/cratis-screenplay-command-surface/SKILL.md +26 -28
- package/package/corpus/skills/cratis-screenplay-command-surface/references/described-handlers.md +82 -0
- package/package/corpus/skills/cratis-screenplay-command-surface/references/generated-values-and-responses.md +82 -28
- package/package/corpus/skills/cratis-screenplay-command-surface/references/protected-reads.md +113 -0
- package/package/corpus/skills/cratis-screenplay-command-surface/references/shop-generation.md +45 -12
- package/package/corpus/skills/cratis-screenplay-command-surface/references/text-and-match-rules.md +119 -0
- package/package/corpus/skills/cratis-screenplay-command-surface/references/validate-rules.md +14 -4
- package/package/corpus/skills/cratis-screenplay-command-surface/verification.json +10 -3
- package/package/corpus/skills/cratis-screenplay-event-modeling/SKILL.md +12 -12
- package/package/corpus/skills/cratis-screenplay-event-modeling/references/parsed-not-runnable.md +5 -3
- package/package/corpus/skills/cratis-screenplay-event-modeling/verification.json +5 -1
- package/package/corpus/skills/cratis-screenplay-legacy-extraction/SKILL.md +2 -2
- package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/evidence-table-template.md +1 -1
- package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/generated-recovery.md +51 -14
- package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/intent-example.md +5 -0
- package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/loss-report-template.md +2 -1
- package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/rule-modeling.md +6 -3
- package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/signal-to-intent.md +1 -1
- package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +22 -28
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/choosing-generation.md +20 -7
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md +12 -6
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-loop.md +9 -1
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-tools.md +58 -10
- package/package/corpus/skills/cratis-screenplay-model-authoring/verification.json +4 -2
- package/package/corpus/skills/cratis-screenplay-model-review/SKILL.md +2 -5
- package/package/corpus/skills/cratis-screenplay-model-review/references/anti-patterns.md +2 -2
- package/package/corpus/skills/cratis-screenplay-model-review/references/checklist.md +2 -2
- package/package/corpus/skills/cratis-screenplay-model-review/references/quick-self-check.md +2 -1
- package/package/corpus/skills/cratis-screenplay-model-review/references/review-questions.md +3 -2
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/principles.md +1 -1
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/vocabulary-map.md +1 -1
- package/package/corpus/skills/cratis-screenplay-read-surface/SKILL.md +2 -3
- package/package/corpus/skills/cratis-screenplay-read-surface/references/named-keys.md +3 -1
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/SKILL.md +8 -3
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/gap-fill-handoff.md +8 -0
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/renderability-gate.md +6 -4
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/SKILL.md +36 -29
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/berth-reservations.md +6 -0
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/invoicing-dues-design.md +15 -4
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/invoicing-dues-example.md +25 -20
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/public-event-coverage.md +14 -3
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/scenario-examples.md +9 -3
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/spec-forms-by-mode.md +7 -3
- package/package/corpus/skills/cratis-screenplay-slice-design/SKILL.md +3 -1
- package/package/corpus/skills/cratis-screenplay-slice-design/references/command-inventory.md +4 -4
- package/package/corpus/skills/cratis-screenplay-slice-design/references/rule-layers.md +4 -2
- package/package/corpus/skills/cratis-screenplay-slice-design/references/slicing.md +4 -3
- package/package/corpus/skills/cratis-screenplay-slice-design/references/worked-example.md +8 -8
- package/package/corpus/skills/cratis-screenplay-specifications/SKILL.md +49 -47
- package/package/corpus/skills/cratis-screenplay-specifications/references/actions-beyond-commands.md +11 -1
- package/package/corpus/skills/cratis-screenplay-specifications/references/generated-fixtures.md +48 -17
- package/package/corpus/skills/cratis-screenplay-specifications/references/vocabulary.md +14 -1
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/SKILL.md +25 -21
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/boundaries.md +2 -2
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/consistency-and-concurrency.md +14 -6
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/multi-service-models.md +4 -3
- package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/streams-example.md +7 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +30 -22
- package/package/corpus/skills/cratis-screenplay-toolchain/references/cheat-sheet-example.md +3 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/cheat-sheet.md +47 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/references/diagnostic-arguments.md +60 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/diagnostics.md +65 -5
- package/package/corpus/skills/cratis-screenplay-toolchain/references/executable-subset.md +50 -22
- package/package/corpus/skills/cratis-screenplay-toolchain/references/generation-checks.md +93 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/generation-strategies-example.md +117 -35
- package/package/corpus/skills/cratis-screenplay-toolchain/references/language-admission.md +46 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/preferred-syntax.md +3 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/renderable-subset.md +25 -10
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +1800 -50
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/traps.md +5 -3
- package/package/corpus/skills/cratis-screenplay-toolchain/verification.json +6 -1
- package/package/corpus/skills/cratis-screenplay-ui-composition/SKILL.md +8 -53
- package/package/corpus/skills/cratis-screenplay-ui-composition/references/file-references.md +2 -2
- package/package/corpus/skills/cratis-screenplay-ui-composition/references/interaction-example.md +54 -0
- package/package/corpus/skills/cratis-screenplay-ui-composition/verification.json +6 -3
- package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +3 -2
- package/package/corpus/skills/cratis-specifications-csharp/references/application-scenarios.md +1 -1
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +30 -29
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +36 -9
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/choosing-generation.md +28 -8
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/publication-and-recovery.md +31 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/sandbox-and-specrunner.md +8 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/verification.json +4 -1
- package/package.json +1 -1
|
@@ -89,14 +89,14 @@ public class and_all_information_is_valid : Specification
|
|
|
89
89
|
#endif
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
(`CommandScenario` event assertions are extension methods keyed by command + event type — `await _scenario.ShouldHaveAppendedEvent<TCommand, TEvent>(eventSourceId[, predicate])`; seed prior state through `_scenario.Given.ForEventSource(id).Events(...)` or `_scenario.Services`. The mode matters: in legacy mode `Given` materializes an in-memory read-model dictionary only, so constraint preconditions go through `EventScenario`'s `Given`; in
|
|
92
|
+
(`CommandScenario` event assertions are extension methods keyed by command + event type — `await _scenario.ShouldHaveAppendedEvent<TCommand, TEvent>(eventSourceId[, predicate])`; seed prior state through `_scenario.Given.ForEventSource(id).Events(...)` or `_scenario.Services`. The mode matters: in legacy mode `Given` materializes an in-memory read-model dictionary only, so constraint preconditions go through `EventScenario`'s `Given`; in decision mode (selected automatically from the command's read-protection mode and dependencies) `Given` writes to the scenario's real log and `EventScenario` is unavailable (accessing it throws), so seed everything through `Given`. Check which mode the spec's scenario uses; see `specs.scenarios.csharp.md` and `cratis-application-slice-specifications`.)
|
|
93
93
|
|
|
94
94
|
### What to specify
|
|
95
95
|
|
|
96
96
|
0. **Every contract specification first** — one spec each, named after it, using the contract's example values. Then add the code-derived cases below that the contract lacks and report them as proposals for the contract (the `.play` model when one covers the slice, otherwise the agreed outline); never present them as contract coverage. A `.play` specification maps to a plain-call spec for a pure decision, with a scenario added where the pipeline, validation, constraints or projections matter; never edit, skip or delete a spec derived from the contract to make code pass — change the code or return an edit request.
|
|
97
97
|
1. **Happy path** — succeeds, correct event(s) appended.
|
|
98
98
|
2. **Each validation failure** — assert **both** `ShouldNotBeSuccessful()` and `ShouldHaveValidationErrors()`. Do not assert on message strings by default; assert exact text only when that wording is the specified behavior, and name that behavior in the fact. Violate **only** the rule the spec is named after (build the command valid in every other respect), or a neighbouring rule makes it pass.
|
|
99
|
-
3. **Business-rule violations** — each `Result<,>` rejection / guarded decision condition. Plain injection is advisory; for `[ProtectedDecision]` or stream decisions use `
|
|
99
|
+
3. **Business-rule violations** — each `Result<,>` rejection / guarded decision condition. Plain injection under `Off` is advisory; for read protection `On`, `[ProtectedDecision]` or stream decisions use `new CommandScenario<T>()` with seeded events, `AppendConcurrently` and `ShouldHaveValidationErrorBecauseOf(ValidationResultReason.ConcurrencyViolation)`, not pinned models and not `UseDecisionReads()`.
|
|
100
100
|
4. **Constraint violations** — `ShouldHaveConstraintViolationFor(name)` via `EventScenario`.
|
|
101
101
|
5. **Authorization** — `ShouldNotBeAuthorized()` (an unauthorized result has no validation errors).
|
|
102
102
|
6. **Repeat execution**, when a reactor invokes the command — the same command twice for the same source; specify whether the second is rejected, a no-op, or appends again.
|
|
@@ -32,7 +32,7 @@ Pick exactly one type per slice folder — determined by what the slice *does*.
|
|
|
32
32
|
|
|
33
33
|
| Type | What it does | Contents |
|
|
34
34
|
| --- | --- | --- |
|
|
35
|
-
| **State Change** | Accepts a command, appends events | Command + validator + event(s); optional `[Passive]` for a current read; a rule that must hold under concurrency needs a decision
|
|
35
|
+
| **State Change** | Accepts a command, appends events | Command + validator + event(s); optional `[Passive]` for a current read; a rule that must hold under concurrency needs read protection `On`, a stream decision or a constraint (see the `cratis-chronicle-decision-reads` skill, `references/choosing-how-a-rule-holds.md`) |
|
|
36
36
|
| **State View** | Builds a queryable read model from events | `[ReadModel]` + projection mappings or reducer code + static query method(s) on the record |
|
|
37
37
|
| **Automation** | Reacts to events, calls external systems / `ICommandPipeline`, or returns follow-up commands/events (including to another stream) | Reactor only |
|
|
38
38
|
| **Translation** | Takes data from outside our own facts (an external system or another service) and records it as our facts | An adapter at the boundary, often a reactor |
|
|
@@ -46,7 +46,7 @@ Commands are imperative intents (`Register`, `Create`); the slice folder is the
|
|
|
46
46
|
Tagged **[contract]** (framework-enforced) or **[convention]** (house default). Mechanics and examples live in `vertical-slices.md`.
|
|
47
47
|
|
|
48
48
|
1. **[contract] Model-bound — no controllers.** Commands are `[Command]` records with a public instance `Handle()` (Arc analyzers enforce this); queries are `static` methods on `[ReadModel]` records; projections/constraints/authorization use attributes. Arc generates the HTTP surface. For read-model style follow rule 11's ladder; for constraints use `IConstraint` when attributes cannot express the rule.
|
|
49
|
-
2. **[contract] Command validation & data flow.** Put command rejection in `CommandValidator<T>`, global value invariants in `ConceptValidator<T>`, and fetched/computed handler data in **`Provide()`** (runs after validation/authorization; may short-circuit with `ValidationResult.Error` / `Result<TProvided, ValidationResult>`). Keep `Handle()` focused on event construction. For a state-dependent rule that must hold **under concurrency**,
|
|
49
|
+
2. **[contract] Command validation & data flow.** Put command rejection in `CommandValidator<T>`, global value invariants in `ConceptValidator<T>`, and fetched/computed handler data in **`Provide()`** (runs after validation/authorization; may short-circuit with `ValidationResult.Error` / `Result<TProvided, ValidationResult>`). Keep `Handle()` focused on event construction. For a state-dependent rule that must hold **under concurrency**, start new code with read protection `On` (`<CratisReadProtection>On</CratisReadProtection>`): put the rule in a `CommandValidator<T>` with the read model as a constructor parameter and return the events from `Handle()`. Use a constraint for uniqueness, a per-stream read model or `IStreamReads`/`StreamDecision` for one stream, an aggregate for a rich lifecycle; `[ProtectedDecision]` with `DecisionRead<T>` is the older per-command opt-in. Under `Off` (the default when nothing is set) a validator over a plain read is a hint, not a guard. Return a typed rejection via `Result<TEvent, ValidationResult>` where needed; the result alone adds no guard. **Throwing from `Provide()`/`Handle()` is an exception (HTTP 500), not a validation rejection** — throw only for genuinely exceptional conditions, never for normal business rejection.
|
|
50
50
|
3. **[contract] Event-source id resolution:** `ICanProvideEventSourceId` first; else the first qualifying property in declaration order — an `EventSourceId`/`EventSourceId<T>`-derived value *or* a `[Key]` property (no priority between them; two candidates is `ARCCHR0002`); else Arc generates one. A value actually used as a Chronicle stream identity derives from `EventSourceId<T>` with the underlying `IComparable` primitive — never `ConceptAs<Guid>` for that stream identity. `NotSet`, `New()`, and primitive→derived-id operators are optional domain/API conveniences, not Chronicle requirements; typed empty/zero values are real specified stream IDs, not `EventSourceId.Unspecified`.
|
|
51
51
|
4. **[contract] Events never carry the event-source id** — it is implicit in the event context.
|
|
52
52
|
5. **[contract] `[Key]` / `[Subject]` are distinct.** `[Key]` is for event-source/read-model/projection key resolution; `[Subject]` is compliance identity only. Don't put either on an `EventSourceId<T>` value (it already is both); use them only for non-`EventSourceId<T>` values.
|
|
@@ -38,8 +38,8 @@ One precise line per load-bearing term, so the same word means the same thing ev
|
|
|
38
38
|
- **Observation command** — a command whose input comes from outside the model's own decisions (webhook, poll, sweep, retry, classifier). It compares with current state and appends nothing when nothing changed; looking is not a fact.
|
|
39
39
|
- **Signal event** — an event appended only to make a reactor do something (`…Requested`, `…Started`, `…Skipped`). Not a domain fact; use a job, state machine, command operation or `ICommandPipeline`.
|
|
40
40
|
- **Constraint** — `IConstraint`; an **append-time** invariant (uniqueness / concurrency) enforced at the event-store level.
|
|
41
|
-
- **DCB (Dynamic Consistency Boundary)** — enforcing a state-dependent rule under concurrency over explicitly enrolled event-state reads: stream decisions/exact scopes or protected, admitted projection decision reads. Plain injected models and `Result<TEvent, ValidationResult>` alone are advisory, not a guard.
|
|
42
|
-
- **Consistency boundary** — the scope within which an invariant holds atomically (an event source, a routed stream, or the event state enrolled by an admitted protected
|
|
41
|
+
- **DCB (Dynamic Consistency Boundary)** — enforcing a state-dependent rule under concurrency over explicitly enrolled event-state reads: read-model parameters under Arc read protection `On`, stream decisions/exact scopes, or protected, admitted projection decision reads. Plain injected models under `Off` (the default) and `Result<TEvent, ValidationResult>` alone are advisory, not a guard.
|
|
42
|
+
- **Consistency boundary** — the scope within which an invariant holds atomically (an event source, a routed stream, or the event state enrolled by an admitted protected read).
|
|
43
43
|
- **EventForEventSourceId** — self-describing wrapper to append an event to a **specific** (cross-stream) event source, from a command `Handle()` or from a reactor handler (reactor support has shipped since Chronicle 15.35). It carries the event stream type and id, source type, subject, occurred time, tags and causation, so it is also how a reactor sets those explicitly.
|
|
44
44
|
- **ReactorDelivery** — the identity of one delivery of one event to one reactor partition; declare it as a handler parameter and Chronicle passes it in. Stable across a replay and across recovering a failed partition, so a receipt kept under its `Id` is what makes a side effect survive re-delivery. An identity, not a guarantee — Chronicle does not know whether the effect ran.
|
|
45
45
|
|
package/package/corpus/harnesses/copilot/instructions/specs.scenarios.csharp.instructions.md
CHANGED
|
@@ -117,10 +117,13 @@ arguments?, paging?, sorting?)`; `Observe(...)` returns `QueryObservation.Initia
|
|
|
117
117
|
and buffered `Next()`. Configure principal/policies in `Services`; schemes fail
|
|
118
118
|
closed without HTTP. Chronicle query `Given` can seed keyed models/events.
|
|
119
119
|
|
|
120
|
-
For guarded commands use `
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
For guarded commands use plain `new CommandScenario<T>()`: it selects the decision
|
|
121
|
+
mode from the command's read-protection mode and dependencies (`UseDecisionReads()`
|
|
122
|
+
is hidden and unnecessary). Given state, guarded reads and appends share one real
|
|
123
|
+
in-process log, unlike legacy mode above; an `Off` command with no protected
|
|
124
|
+
dependency stays in legacy mode. Pinned models cannot supply guards. Use `OnRoute(route)` and
|
|
125
|
+
`AppendConcurrently` with `ShouldHaveValidationErrorBecauseOf(ValidationResultReason.ConcurrencyViolation)`
|
|
126
|
+
to prove conflicts (there is no `scenario.AppendConditions`); `ShouldHaveCompletedStream` proves
|
|
124
127
|
completion and `DecisionReadAdmissions.ShouldAdmitDecisionReadsIn(assembly)`
|
|
125
128
|
checks admission. See **cratis-application-slice-specifications**,
|
|
126
129
|
`references/query-and-decision-scenarios.md`, for sources and limitations.
|
|
@@ -89,7 +89,7 @@ The command carries input from the caller. `Handle()` is defined directly on the
|
|
|
89
89
|
| Reusable value invariant (length, format, range) | `ConceptValidator<T>` on the concept type |
|
|
90
90
|
| Command-input / cross-field / pre-handler rule (incl. injected read-model/service/identity checks) | `CommandValidator<TCommand>` with `RuleFor(...)` |
|
|
91
91
|
| Handler needs fetched/computed data before it can build the event | `Provide()` — fetches the data; may validate it and short-circuit |
|
|
92
|
-
| State-dependent rule that must hold **under concurrency** | `IStreamReads`/`StreamDecision` for one stream
|
|
92
|
+
| State-dependent rule that must hold **under concurrency** | Read protection `On`: the read model as a `CommandValidator<T>` constructor (or `Handle()`) parameter; a per-stream read model or `IStreamReads`/`StreamDecision` for one stream; an aggregate for a rich lifecycle. `[ProtectedDecision]` + `DecisionRead<T>` is the older per-command opt-in. Under `Off` (default) injection is a hint |
|
|
93
93
|
| Uniqueness | Chronicle `[Unique]` / `IConstraint` (race-safe) — not a read-model pre-check |
|
|
94
94
|
| Genuinely exceptional failure (bug, missing infra) | `throw` a domain exception |
|
|
95
95
|
|
|
@@ -183,13 +183,21 @@ Three attributes scope an append broad→narrow, and each one *also* (only with
|
|
|
183
183
|
|
|
184
184
|
Each attribute always tags the appended events (visible in `EventContext`). **Only when the command also carries `concurrency: true`** does each dimension join the server-side `ConcurrencyScope` (plus the event source id), with the optimistic strategy supplying the expected tail sequence number. A command **without** `concurrency: true` contributes no scope of its own, and the append falls to whatever concurrency strategy the event sequence is configured with — by default the optimistic one, scoped to the event source. So the attribute chooses *which dimensions* bound the check, not whether a check happens at all. Reach for it on high-contention state transitions where a stale read would let two commands both "win" — it narrows the boundary so appends that are genuinely independent stop colliding (concurrency failures surface on `CommandResult`/`AppendResult`). Adopt it deliberately per behavior, not as a blanket default.
|
|
185
185
|
|
|
186
|
-
|
|
187
|
-
`
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
186
|
+
New code sets `<CratisReadProtection>On</CratisReadProtection>` (Arc 22.106.0).
|
|
187
|
+
Under `On`, a read-model parameter of `Handle()` or of a `CommandValidator<T>`
|
|
188
|
+
constructor is read fresh and guards the append; the state rule goes in the
|
|
189
|
+
validator. Two entities: two parameters, `[LookUpBy(nameof(Cmd.Prop))]` on the
|
|
190
|
+
second, `IEnumerable<EventForEventSourceId>` returned. One stream inside a source:
|
|
191
|
+
a per-stream read model (`[ReadModel, EventStream<TSource>("Stream")]`) on a command
|
|
192
|
+
routed with `[EventSource<TSource>("Stream")]` and `[EventStreamId("{Prop}")]`, or
|
|
193
|
+
`IStreamReads.ForCommand()` in `Provide()` returning `StreamDecision.Append(...)`.
|
|
194
|
+
A deliberate advisory read is `[Unprotected("why")]`. Models must be admitted
|
|
195
|
+
(source-keyed projection with a string or Guid `Id`, or an unfiltered reducer
|
|
196
|
+
without constructor dependencies); joins, children and event-property keys are
|
|
197
|
+
refused. Under `Off` (the default) injection and keyed reads are advisory,
|
|
198
|
+
including in `Handle()`; `[ProtectedDecision]` + `DecisionRead<T>` is the older
|
|
199
|
+
per-command opt-in and still works. Exact returned scopes replace automatic
|
|
200
|
+
`concurrency: true` capture. Refusals: [choosing how a rule holds](../../../skills/cratis-chronicle-decision-reads/references/choosing-how-a-rule-holds.md).
|
|
193
201
|
|
|
194
202
|
`[EventStreamId("{ReportingScopeId}:{Period}")]` resolves command property values
|
|
195
203
|
after validation; concepts supply their underlying values. An interface can
|
|
@@ -296,7 +304,7 @@ query reference for versioned behavior and composition trade-offs.
|
|
|
296
304
|
|
|
297
305
|
### Choosing read-model access in command-side code **[convention]**
|
|
298
306
|
|
|
299
|
-
Ladder, first that fits: (1)
|
|
307
|
+
Ladder, first that fits: (1) direct read-model injection — guarded under read protection `On`, advisory under `Off`; keyed by the command's own event-source id, or by `[LookUpBy]`; (2) a `[Passive]` projection; (3) `IReadModels.GetInstanceById<T>((EventSourceId)key)` for a different/derived key; (4) a materialized projection. A lookup *interface* is justified **only** for a genuine non-key search (e.g. find-by-email) — never to wrap a keyed `GetInstanceById`.
|
|
300
308
|
|
|
301
309
|
**[contract] Passive state is not sink state.** `IMongoCollection<T>` cannot
|
|
302
310
|
compute a passive read model; any leftover documents are not maintained state.
|
|
@@ -408,14 +416,15 @@ Redaction is a system-event request that removes payload availability from the e
|
|
|
408
416
|
|
|
409
417
|
## Cross-slice patterns **[convention]**
|
|
410
418
|
|
|
411
|
-
Cross-slice access is **read-only and goes through Chronicle** — inject another slice's read model (validator/`Provide()`/`Handle()`), read by explicit key with `IReadModels.GetInstanceById`, mark a `[Passive]` projection for a current read (a rule that must hold under concurrency needs a decision
|
|
419
|
+
Cross-slice access is **read-only and goes through Chronicle** — inject another slice's read model (validator/`Provide()`/`Handle()`), read by explicit key with `IReadModels.GetInstanceById`, mark a `[Passive]` projection for a current read (a rule that must hold under concurrency needs read protection `On`, a stream decision or a constraint, see the `cratis-chronicle-decision-reads` skill's `references/choosing-how-a-rule-holds.md`), or react to its events. **Never** instantiate or DI another slice's command/handler/service.
|
|
412
420
|
|
|
413
421
|
**DCB (Dynamic Consistency Boundary):** the boundary is the event state explicitly
|
|
414
422
|
enrolled in guarded decisions, not every model a command reads. Use stream
|
|
415
|
-
reads/exact scopes or
|
|
416
|
-
|
|
423
|
+
reads/exact scopes or read-model parameters under read protection `On`
|
|
424
|
+
(`[ProtectedDecision]` + `DecisionRead<T>` is the older per-command opt-in);
|
|
425
|
+
validator/`Provide()`/`Handle()` injection under `Off` is advisory. Stream-level
|
|
417
426
|
`concurrency: true` scopes are another explicit boundary, not protection for an
|
|
418
|
-
arbitrary snapshot. Specify conflicts with `
|
|
427
|
+
arbitrary snapshot. Specify conflicts with `AppendConcurrently` and actual seeded
|
|
419
428
|
events, not pinned models.
|
|
420
429
|
|
|
421
430
|
## Dialogs
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
],
|
|
47
47
|
"rules": []
|
|
48
48
|
},
|
|
49
|
-
"notes": "Source-checked Stage v4.103.0: exact ESM v1-v17 and v19, not v18 or v20. Snapshot/live collection queries, identity-mapping all projections, stored event names, event-id publication checks, release-baseline warnings, command clock/receipt-time generation, conditional and multi-command slices, generated text constraints, no-event and bounded redelivery specs, refusal branches. Query concept validation still uses primitive workaround; Stage#370
|
|
49
|
+
"notes": "Source-checked Stage v4.103.0: exact ESM v1-v17 and v19, not v18 or v20 (v18 admitted from 4.106.0 and v20 from 4.109.0, taught with Requires lines; 4.104-4.108 not otherwise reviewed). Snapshot/live collection queries, identity-mapping all projections, stored event names, event-id publication checks, release-baseline warnings, command clock/receipt-time generation, conditional and multi-command slices, generated text constraints, no-event and bounded redelivery specs, refusal branches. Query concept validation still uses primitive workaround; Stage#370 released in 4.106.0. CLI v3.62.0 bundles Stage 4.99.0, not 4.103.0. Generated-values-only delta checked at v4.127.0: v21/all-Proven unpinned specs from 4.115.0, warning/info generated rules 4.117.0, checksum validation 4.118.0, pattern/random factories/Host/executor 4.119.0; generate rule still STAGE-ESM-028. Language admission reconciled at the same v4.127.0 tag: exact v1-v21 and v23-v25 pairs; v22/v26 and mixed pairs -016, protected reads -044, atomic mode -043, described bodies STAGE-IMPL-002 even Stub mode; v24 first admitted 4.124.0 (absent 4.123.0). Other deltas not reviewed; synced unchanged. No new render/browser/Docker probe."
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
"id": "arc",
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
"cratis-arc-observable-query-http",
|
|
82
82
|
"cratis-arc-query-paging",
|
|
83
83
|
"cratis-arc-react-page",
|
|
84
|
+
"cratis-chronicle-decision-reads",
|
|
84
85
|
"cratis-chronicle-read-model",
|
|
85
86
|
"cratis-code-review",
|
|
86
87
|
"cratis-lens-browser-extension",
|
|
@@ -98,7 +99,7 @@
|
|
|
98
99
|
"vertical-slices.md"
|
|
99
100
|
]
|
|
100
101
|
},
|
|
101
|
-
"notes": "
|
|
102
|
+
"notes": "Whole-product floor unchanged at 22.100.0. Focused read-protection coverage, Arc 22.106.0-22.108.0: ReadProtectionMode Off, Measure and On (MSBuild property, assembly and command attributes, runtime MaximumMode cap), read-model parameters of Handle and validator constructors guarded under On, [LookUpBy], [Unprotected(\"why\")], per-stream read models on routed commands, Measure report, ReadChanged and ReadIncomplete refusals, and CommandScenario selecting decision mode automatically. the caller severity threshold (X-Allowed-Severity, [BlockOnValidationSeverity]) and the Error floor for commands declared On (22.108.0). [ProtectedDecision] and DecisionRead<T> retained as the per-command opt-in. The other 22.101.0-22.108.0 changes were not reviewed. Prior notes: scoped completion and routing validation, generated-target freshness, observable scenarios, asynchronous execution scopes, read-model diagnostics, ordered reactor refusal handling and ICommandExceptionMapper. Generated-values-only delta checked at v22.107.1: 22.104.0 enforces every concept-rule severity and carries Fundamentals 7.25.0; 22.107.0 recovers pattern/random/checksum and self-contained String generation rules, with v21 caps and fixture validation. No new runtime probe."
|
|
102
103
|
},
|
|
103
104
|
{
|
|
104
105
|
"id": "ai",
|
|
@@ -170,9 +171,9 @@
|
|
|
170
171
|
}
|
|
171
172
|
],
|
|
172
173
|
"synced": {
|
|
173
|
-
"version": "4.
|
|
174
|
+
"version": "4.198.0",
|
|
174
175
|
"date": "2026-10-16",
|
|
175
|
-
"by": "Cratis/AI#
|
|
176
|
+
"by": "Cratis/AI#576"
|
|
176
177
|
},
|
|
177
178
|
"covers": {
|
|
178
179
|
"skills": [
|
|
@@ -200,7 +201,7 @@
|
|
|
200
201
|
],
|
|
201
202
|
"rules": []
|
|
202
203
|
},
|
|
203
|
-
"notes": "Standalone 4.
|
|
204
|
+
"notes": "Standalone 4.198.0 and byte-identical compiler contract pinned. Cratis/AI#576: mechanical compiler catch-up. Language batch #717/#722/#723/#724/#720/#728/#734 teaches atomic reaction batches (v22), described payload handlers (v23), portable linear matching, ordinal text rules (v24), protected command reads (v25), diagnostic arguments and event-source invokes mappings. Facts read at Screenplay v4.198.0 and introducing tags; Stage v4.127.0 admits exact v1-v21 and v23-v25 pairs but refuses v22/v26 and mixed pairs (STAGE-ESM-016), protected reads (044), atomic mode (043) and described handlers (STAGE-IMPL-002), even Stub mode. Stage text rules execute/render; no new renderer/runtime probe or Stage pin uplift. Cratis/AI#706: application navigation regions (module inherit, extend or override), toolbar slice buttons, blueprint template references and sample data packs in cratis-screenplay-ui-composition (references/application-shell.md), with the canonical shell vector and the rejection codes PLAY0710-PLAY0722 and PLAY0740-PLAY0765. Examples moved to the executable-state rules since 4.164: translator reactions in an Automation slice beside the Translate slice (PLAY0694) and the first record of a capture key appended with `when added`. Earlier: Standalone 4.164.0 and byte-identical compiler contract pinned. Cratis/AI#663: one origin per capture (PLAY0687). #668: executable no-event/redelivery v17. #684: concept query-argument validation in every version and query-error assertions v18. #681: executable refusal branches and refusal values v19. #666: proposal summary/severity counts and exact structural source proposals. Generated-concept rules select v20 with PLAY0688/0689 proofs and derived-matches repair; generated secrets and preview strategies remain refused. Release-baseline from-esm admission reads storedName. Standalone binding is not renderer admission: Stage 4.103.0 admits v1-v17 and v19; CLI 3.62.0 bundles Screenplay 4.159.0 / Stage 4.99.0 and renders through v16. Prior evidence remains unless superseded; no new renderer, Docker or browser probe."
|
|
204
205
|
},
|
|
205
206
|
{
|
|
206
207
|
"id": "scene",
|
|
@@ -322,7 +323,7 @@
|
|
|
322
323
|
],
|
|
323
324
|
"rules": []
|
|
324
325
|
},
|
|
325
|
-
"notes": "
|
|
326
|
+
"notes": "Whole-product floor unchanged at 19.65.0. Focused read-protection coverage, Chronicle 19.68.0-19.71.x: admitted reducers (unfiltered, no constructor dependencies, 19.68.0), per-stream read models with [EventStream<TSource>], IUnitOfWork.EnforceReadProtection, AllowReads and GetAppendConditions, CHR0062-CHR0064. The other 19.66.0-19.71.x changes were not reviewed. Prior notes: explicit artifacts, collection observation, redelivery scenarios, webhook generation pins, enrollment contributors, seed ownership, reconnect readiness and hosting/storage fixes. No new kernel/transport probe."
|
|
326
327
|
},
|
|
327
328
|
{
|
|
328
329
|
"id": "arc-go",
|
|
@@ -380,9 +381,9 @@
|
|
|
380
381
|
}
|
|
381
382
|
],
|
|
382
383
|
"synced": {
|
|
383
|
-
"version": "7.
|
|
384
|
-
"date": "2026-10-
|
|
385
|
-
"by": "Cratis/AI#
|
|
384
|
+
"version": "7.25.0",
|
|
385
|
+
"date": "2026-10-15",
|
|
386
|
+
"by": "Cratis/AI#576"
|
|
386
387
|
},
|
|
387
388
|
"covers": {
|
|
388
389
|
"skills": [
|
|
@@ -398,7 +399,7 @@
|
|
|
398
399
|
"concepts.md"
|
|
399
400
|
]
|
|
400
401
|
},
|
|
401
|
-
"notes": "
|
|
402
|
+
"notes": "Source-checked v7.25.0: IGeneratable/IGeneratableInContext, GenerationContext, UUID/ULID/TypeID/NanoID/bytes and finite GenerateValue.Pattern overloads with optional Checksum, inclusive Int64 Integer, and HasValidChecksum. Pattern date segments use captured UTC time; check digits add no entropy or authorization and do not automatically add validation. No Stage version uplift or generated-secret admission is implied."
|
|
402
403
|
},
|
|
403
404
|
{
|
|
404
405
|
"id": "components",
|
|
@@ -16,7 +16,7 @@ Enforce a new rule on an existing command. Invoke the **cratis-arc-command-valid
|
|
|
16
16
|
- Reusable value invariant → `ConceptValidator<T>`.
|
|
17
17
|
- Command-input / cross-field / pre-handler rule → `CommandValidator<T>`.
|
|
18
18
|
- Handler needs fetched data first → `Provide()`.
|
|
19
|
-
- State rule that must hold **under concurrency** →
|
|
19
|
+
- State rule that must hold **under concurrency** → new code sets read protection `On` (`<CratisReadProtection>On</CratisReadProtection>`): the read model is a `CommandValidator<T>` constructor parameter and `Handle()` returns the events; add `[LookUpBy]` for a second entity, a per-stream read model or `IStreamReads`/`StreamDecision` for one stream. `[ProtectedDecision]` with `DecisionRead<T>` is the older per-command opt-in. Under `Off` (the default) a validator over a plain read is a hint, and `Result<TEvent, ValidationResult>` alone adds no guard. Specify the race with `AppendConcurrently` and seeded events, not pinned models.
|
|
20
20
|
- Uniqueness → `[Unique]` / `IConstraint`.
|
|
21
21
|
|
|
22
22
|
> **Never throw for normal business rejection** — a throw is HTTP 500, not a validation error. Recoverable rejection is a `ValidationResult` / `Result<,>`. Add a spec for the failure case (assert both not-successful and has-validation-errors). The skill carries the detail; don't duplicate it here.
|
|
@@ -31,7 +31,7 @@ Pick exactly one type per slice folder — determined by what the slice *does*.
|
|
|
31
31
|
|
|
32
32
|
| Type | What it does | Contents |
|
|
33
33
|
| --- | --- | --- |
|
|
34
|
-
| **State Change** | Accepts a command, appends events | Command + validator + event(s); optional `[Passive]` for a current read; a rule that must hold under concurrency needs a decision
|
|
34
|
+
| **State Change** | Accepts a command, appends events | Command + validator + event(s); optional `[Passive]` for a current read; a rule that must hold under concurrency needs read protection `On`, a stream decision or a constraint (see the `cratis-chronicle-decision-reads` skill, `references/choosing-how-a-rule-holds.md`) |
|
|
35
35
|
| **State View** | Builds a queryable read model from events | `[ReadModel]` + projection mappings or reducer code + static query method(s) on the record |
|
|
36
36
|
| **Automation** | Reacts to events, calls external systems / `ICommandPipeline`, or returns follow-up commands/events (including to another stream) | Reactor only |
|
|
37
37
|
| **Translation** | Takes data from outside our own facts (an external system or another service) and records it as our facts | An adapter at the boundary, often a reactor |
|
|
@@ -45,7 +45,7 @@ Commands are imperative intents (`Register`, `Create`); the slice folder is the
|
|
|
45
45
|
Tagged **[contract]** (framework-enforced) or **[convention]** (house default). Mechanics and examples live in `vertical-slices.md`.
|
|
46
46
|
|
|
47
47
|
1. **[contract] Model-bound — no controllers.** Commands are `[Command]` records with a public instance `Handle()` (Arc analyzers enforce this); queries are `static` methods on `[ReadModel]` records; projections/constraints/authorization use attributes. Arc generates the HTTP surface. For read-model style follow rule 11's ladder; for constraints use `IConstraint` when attributes cannot express the rule.
|
|
48
|
-
2. **[contract] Command validation & data flow.** Put command rejection in `CommandValidator<T>`, global value invariants in `ConceptValidator<T>`, and fetched/computed handler data in **`Provide()`** (runs after validation/authorization; may short-circuit with `ValidationResult.Error` / `Result<TProvided, ValidationResult>`). Keep `Handle()` focused on event construction. For a state-dependent rule that must hold **under concurrency**,
|
|
48
|
+
2. **[contract] Command validation & data flow.** Put command rejection in `CommandValidator<T>`, global value invariants in `ConceptValidator<T>`, and fetched/computed handler data in **`Provide()`** (runs after validation/authorization; may short-circuit with `ValidationResult.Error` / `Result<TProvided, ValidationResult>`). Keep `Handle()` focused on event construction. For a state-dependent rule that must hold **under concurrency**, start new code with read protection `On` (`<CratisReadProtection>On</CratisReadProtection>`): put the rule in a `CommandValidator<T>` with the read model as a constructor parameter and return the events from `Handle()`. Use a constraint for uniqueness, a per-stream read model or `IStreamReads`/`StreamDecision` for one stream, an aggregate for a rich lifecycle; `[ProtectedDecision]` with `DecisionRead<T>` is the older per-command opt-in. Under `Off` (the default when nothing is set) a validator over a plain read is a hint, not a guard. Return a typed rejection via `Result<TEvent, ValidationResult>` where needed; the result alone adds no guard. **Throwing from `Provide()`/`Handle()` is an exception (HTTP 500), not a validation rejection** — throw only for genuinely exceptional conditions, never for normal business rejection.
|
|
49
49
|
3. **[contract] Event-source id resolution:** `ICanProvideEventSourceId` first; else the first qualifying property in declaration order — an `EventSourceId`/`EventSourceId<T>`-derived value *or* a `[Key]` property (no priority between them; two candidates is `ARCCHR0002`); else Arc generates one. A value actually used as a Chronicle stream identity derives from `EventSourceId<T>` with the underlying `IComparable` primitive — never `ConceptAs<Guid>` for that stream identity. `NotSet`, `New()`, and primitive→derived-id operators are optional domain/API conveniences, not Chronicle requirements; typed empty/zero values are real specified stream IDs, not `EventSourceId.Unspecified`.
|
|
50
50
|
4. **[contract] Events never carry the event-source id** — it is implicit in the event context.
|
|
51
51
|
5. **[contract] `[Key]` / `[Subject]` are distinct.** `[Key]` is for event-source/read-model/projection key resolution; `[Subject]` is compliance identity only. Don't put either on an `EventSourceId<T>` value (it already is both); use them only for non-`EventSourceId<T>` values.
|
|
@@ -37,8 +37,8 @@ One precise line per load-bearing term, so the same word means the same thing ev
|
|
|
37
37
|
- **Observation command** — a command whose input comes from outside the model's own decisions (webhook, poll, sweep, retry, classifier). It compares with current state and appends nothing when nothing changed; looking is not a fact.
|
|
38
38
|
- **Signal event** — an event appended only to make a reactor do something (`…Requested`, `…Started`, `…Skipped`). Not a domain fact; use a job, state machine, command operation or `ICommandPipeline`.
|
|
39
39
|
- **Constraint** — `IConstraint`; an **append-time** invariant (uniqueness / concurrency) enforced at the event-store level.
|
|
40
|
-
- **DCB (Dynamic Consistency Boundary)** — enforcing a state-dependent rule under concurrency over explicitly enrolled event-state reads: stream decisions/exact scopes or protected, admitted projection decision reads. Plain injected models and `Result<TEvent, ValidationResult>` alone are advisory, not a guard.
|
|
41
|
-
- **Consistency boundary** — the scope within which an invariant holds atomically (an event source, a routed stream, or the event state enrolled by an admitted protected
|
|
40
|
+
- **DCB (Dynamic Consistency Boundary)** — enforcing a state-dependent rule under concurrency over explicitly enrolled event-state reads: read-model parameters under Arc read protection `On`, stream decisions/exact scopes, or protected, admitted projection decision reads. Plain injected models under `Off` (the default) and `Result<TEvent, ValidationResult>` alone are advisory, not a guard.
|
|
41
|
+
- **Consistency boundary** — the scope within which an invariant holds atomically (an event source, a routed stream, or the event state enrolled by an admitted protected read).
|
|
42
42
|
- **EventForEventSourceId** — self-describing wrapper to append an event to a **specific** (cross-stream) event source, from a command `Handle()` or from a reactor handler (reactor support has shipped since Chronicle 15.35). It carries the event stream type and id, source type, subject, occurred time, tags and causation, so it is also how a reactor sets those explicitly.
|
|
43
43
|
- **ReactorDelivery** — the identity of one delivery of one event to one reactor partition; declare it as a handler parameter and Chronicle passes it in. Stable across a replay and across recovering a failed partition, so a receipt kept under its `Id` is what makes a side effect survive re-delivery. An identity, not a guarantee — Chronicle does not know whether the effect ran.
|
|
44
44
|
|
|
@@ -116,10 +116,13 @@ arguments?, paging?, sorting?)`; `Observe(...)` returns `QueryObservation.Initia
|
|
|
116
116
|
and buffered `Next()`. Configure principal/policies in `Services`; schemes fail
|
|
117
117
|
closed without HTTP. Chronicle query `Given` can seed keyed models/events.
|
|
118
118
|
|
|
119
|
-
For guarded commands use `
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
For guarded commands use plain `new CommandScenario<T>()`: it selects the decision
|
|
120
|
+
mode from the command's read-protection mode and dependencies (`UseDecisionReads()`
|
|
121
|
+
is hidden and unnecessary). Given state, guarded reads and appends share one real
|
|
122
|
+
in-process log, unlike legacy mode above; an `Off` command with no protected
|
|
123
|
+
dependency stays in legacy mode. Pinned models cannot supply guards. Use `OnRoute(route)` and
|
|
124
|
+
`AppendConcurrently` with `ShouldHaveValidationErrorBecauseOf(ValidationResultReason.ConcurrencyViolation)`
|
|
125
|
+
to prove conflicts (there is no `scenario.AppendConditions`); `ShouldHaveCompletedStream` proves
|
|
123
126
|
completion and `DecisionReadAdmissions.ShouldAdmitDecisionReadsIn(assembly)`
|
|
124
127
|
checks admission. See **cratis-application-slice-specifications**,
|
|
125
128
|
`references/query-and-decision-scenarios.md`, for sources and limitations.
|
|
@@ -88,7 +88,7 @@ The command carries input from the caller. `Handle()` is defined directly on the
|
|
|
88
88
|
| Reusable value invariant (length, format, range) | `ConceptValidator<T>` on the concept type |
|
|
89
89
|
| Command-input / cross-field / pre-handler rule (incl. injected read-model/service/identity checks) | `CommandValidator<TCommand>` with `RuleFor(...)` |
|
|
90
90
|
| Handler needs fetched/computed data before it can build the event | `Provide()` — fetches the data; may validate it and short-circuit |
|
|
91
|
-
| State-dependent rule that must hold **under concurrency** | `IStreamReads`/`StreamDecision` for one stream
|
|
91
|
+
| State-dependent rule that must hold **under concurrency** | Read protection `On`: the read model as a `CommandValidator<T>` constructor (or `Handle()`) parameter; a per-stream read model or `IStreamReads`/`StreamDecision` for one stream; an aggregate for a rich lifecycle. `[ProtectedDecision]` + `DecisionRead<T>` is the older per-command opt-in. Under `Off` (default) injection is a hint |
|
|
92
92
|
| Uniqueness | Chronicle `[Unique]` / `IConstraint` (race-safe) — not a read-model pre-check |
|
|
93
93
|
| Genuinely exceptional failure (bug, missing infra) | `throw` a domain exception |
|
|
94
94
|
|
|
@@ -182,13 +182,21 @@ Three attributes scope an append broad→narrow, and each one *also* (only with
|
|
|
182
182
|
|
|
183
183
|
Each attribute always tags the appended events (visible in `EventContext`). **Only when the command also carries `concurrency: true`** does each dimension join the server-side `ConcurrencyScope` (plus the event source id), with the optimistic strategy supplying the expected tail sequence number. A command **without** `concurrency: true` contributes no scope of its own, and the append falls to whatever concurrency strategy the event sequence is configured with — by default the optimistic one, scoped to the event source. So the attribute chooses *which dimensions* bound the check, not whether a check happens at all. Reach for it on high-contention state transitions where a stale read would let two commands both "win" — it narrows the boundary so appends that are genuinely independent stop colliding (concurrency failures surface on `CommandResult`/`AppendResult`). Adopt it deliberately per behavior, not as a blanket default.
|
|
184
184
|
|
|
185
|
-
|
|
186
|
-
`
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
185
|
+
New code sets `<CratisReadProtection>On</CratisReadProtection>` (Arc 22.106.0).
|
|
186
|
+
Under `On`, a read-model parameter of `Handle()` or of a `CommandValidator<T>`
|
|
187
|
+
constructor is read fresh and guards the append; the state rule goes in the
|
|
188
|
+
validator. Two entities: two parameters, `[LookUpBy(nameof(Cmd.Prop))]` on the
|
|
189
|
+
second, `IEnumerable<EventForEventSourceId>` returned. One stream inside a source:
|
|
190
|
+
a per-stream read model (`[ReadModel, EventStream<TSource>("Stream")]`) on a command
|
|
191
|
+
routed with `[EventSource<TSource>("Stream")]` and `[EventStreamId("{Prop}")]`, or
|
|
192
|
+
`IStreamReads.ForCommand()` in `Provide()` returning `StreamDecision.Append(...)`.
|
|
193
|
+
A deliberate advisory read is `[Unprotected("why")]`. Models must be admitted
|
|
194
|
+
(source-keyed projection with a string or Guid `Id`, or an unfiltered reducer
|
|
195
|
+
without constructor dependencies); joins, children and event-property keys are
|
|
196
|
+
refused. Under `Off` (the default) injection and keyed reads are advisory,
|
|
197
|
+
including in `Handle()`; `[ProtectedDecision]` + `DecisionRead<T>` is the older
|
|
198
|
+
per-command opt-in and still works. Exact returned scopes replace automatic
|
|
199
|
+
`concurrency: true` capture. Refusals: [choosing how a rule holds](../skills/cratis-chronicle-decision-reads/references/choosing-how-a-rule-holds.md).
|
|
192
200
|
|
|
193
201
|
`[EventStreamId("{ReportingScopeId}:{Period}")]` resolves command property values
|
|
194
202
|
after validation; concepts supply their underlying values. An interface can
|
|
@@ -295,7 +303,7 @@ query reference for versioned behavior and composition trade-offs.
|
|
|
295
303
|
|
|
296
304
|
### Choosing read-model access in command-side code **[convention]**
|
|
297
305
|
|
|
298
|
-
Ladder, first that fits: (1)
|
|
306
|
+
Ladder, first that fits: (1) direct read-model injection — guarded under read protection `On`, advisory under `Off`; keyed by the command's own event-source id, or by `[LookUpBy]`; (2) a `[Passive]` projection; (3) `IReadModels.GetInstanceById<T>((EventSourceId)key)` for a different/derived key; (4) a materialized projection. A lookup *interface* is justified **only** for a genuine non-key search (e.g. find-by-email) — never to wrap a keyed `GetInstanceById`.
|
|
299
307
|
|
|
300
308
|
**[contract] Passive state is not sink state.** `IMongoCollection<T>` cannot
|
|
301
309
|
compute a passive read model; any leftover documents are not maintained state.
|
|
@@ -407,14 +415,15 @@ Redaction is a system-event request that removes payload availability from the e
|
|
|
407
415
|
|
|
408
416
|
## Cross-slice patterns **[convention]**
|
|
409
417
|
|
|
410
|
-
Cross-slice access is **read-only and goes through Chronicle** — inject another slice's read model (validator/`Provide()`/`Handle()`), read by explicit key with `IReadModels.GetInstanceById`, mark a `[Passive]` projection for a current read (a rule that must hold under concurrency needs a decision
|
|
418
|
+
Cross-slice access is **read-only and goes through Chronicle** — inject another slice's read model (validator/`Provide()`/`Handle()`), read by explicit key with `IReadModels.GetInstanceById`, mark a `[Passive]` projection for a current read (a rule that must hold under concurrency needs read protection `On`, a stream decision or a constraint, see the `cratis-chronicle-decision-reads` skill's `references/choosing-how-a-rule-holds.md`), or react to its events. **Never** instantiate or DI another slice's command/handler/service.
|
|
411
419
|
|
|
412
420
|
**DCB (Dynamic Consistency Boundary):** the boundary is the event state explicitly
|
|
413
421
|
enrolled in guarded decisions, not every model a command reads. Use stream
|
|
414
|
-
reads/exact scopes or
|
|
415
|
-
|
|
422
|
+
reads/exact scopes or read-model parameters under read protection `On`
|
|
423
|
+
(`[ProtectedDecision]` + `DecisionRead<T>` is the older per-command opt-in);
|
|
424
|
+
validator/`Provide()`/`Handle()` injection under `Off` is advisory. Stream-level
|
|
416
425
|
`concurrency: true` scopes are another explicit boundary, not protection for an
|
|
417
|
-
arbitrary snapshot. Specify conflicts with `
|
|
426
|
+
arbitrary snapshot. Specify conflicts with `AppendConcurrently` and actual seeded
|
|
418
427
|
events, not pinned models.
|
|
419
428
|
|
|
420
429
|
## Dialogs
|
|
@@ -44,7 +44,7 @@ This skill is a method: it names no API of its own. When the Screenplay skills a
|
|
|
44
44
|
| --- | --- |
|
|
45
45
|
| Where each rule lives (validator, `Provide()`, handler result, constraint, exception) | `rules/vertical-slices.md` "The decision matrix" |
|
|
46
46
|
| A customization never makes a rejected model renderable; managed output is never hand-edited | Stage (`Customizations/` is the unmanaged seam; managed output is regenerated) |
|
|
47
|
-
|
|
|
47
|
+
| Attached command handlers remain unbound (PLAY0268); pending handlers with unconditional productions bind v23 as described commands, but reached bodies are Unsupported ImplementationBody and Stage refuses STAGE-IMPL-002 even in Stub mode | Screenplay and Stage; use the model as the gap-fill contract |
|
|
48
48
|
|
|
49
49
|
## Procedure
|
|
50
50
|
|
|
@@ -201,9 +201,10 @@ produces. Pin an instance only when the events would be noise.
|
|
|
201
201
|
Both builder methods return `void`; there is nothing to await.
|
|
202
202
|
|
|
203
203
|
That is **legacy** read-model mode: seeded events populate an in-memory model,
|
|
204
|
-
not the append log.
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
not the append log. A command under read protection `On`, with a per-stream model or
|
|
205
|
+
with `[ProtectedDecision]` selects the decision mode itself (no `UseDecisionReads()`
|
|
206
|
+
call); actual events and decisions then share one in-process log. Pinned models
|
|
207
|
+
cannot supply decision guards. Read
|
|
207
208
|
[query and decision scenarios](references/query-and-decision-scenarios.md) when
|
|
208
209
|
specifying query pipelines, routed conflicts, projection admission or completion.
|
|
209
210
|
|
|
@@ -54,9 +54,18 @@ integration setup: scenario pins do not replace arbitrary stores.
|
|
|
54
54
|
|
|
55
55
|
## Guarded decisions
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
mode
|
|
59
|
-
|
|
57
|
+
Create `new CommandScenario<TCommand>()`. It selects Chronicle's in-process decision
|
|
58
|
+
mode from the command's read-protection mode and dependencies (for example under
|
|
59
|
+
`On`, or with a per-stream read model); do not call `UseDecisionReads()`, which is
|
|
60
|
+
hidden and unnecessary. This mode shares **one real in-process event log** among
|
|
61
|
+
given events, guarded reads, constraints and returned appends. Seed actual events
|
|
62
|
+
(`Given.ForEventSource(id).Events(...)`, or `.OnRoute(route).Events(...)`); protected
|
|
63
|
+
reads fold the log and ignore a pinned `.ReadModel(m)`, which only pins an advisory
|
|
64
|
+
(plain) read. Assert a lost race with
|
|
65
|
+
`result.ShouldHaveValidationErrorBecauseOf(ValidationResultReason.ConcurrencyViolation)`
|
|
66
|
+
after `scenario.AppendConcurrently(id, events...)`. There is no
|
|
67
|
+
`scenario.AppendConditions`. An `Off` command with no protected dependency stays in
|
|
68
|
+
legacy mode.
|
|
60
69
|
`Given.ForEventSource(id).OnRoute(route).Events(...)` preserves routing;
|
|
61
70
|
`scenario.EventRoutes.For<TSource>(stream).WithStreamId(...)` creates the route.
|
|
62
71
|
Queue interference using `AppendConcurrently(id, route, event)` and assert a
|
|
@@ -159,11 +159,15 @@ protect plain HTTP snapshots, waits and long polls; older versions leave them un
|
|
|
159
159
|
|
|
160
160
|
### Validators are not an authorization boundary
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
162
|
+
Authorization runs before validation, so a caller who may not act never reaches a
|
|
163
|
+
validator. Express who may act through policies, roles and `[Authorize]`, not a
|
|
164
|
+
validator. A state rule ("the order is still open") belongs in a
|
|
165
|
+
`CommandValidator<T>` with the read model as a constructor parameter; under read
|
|
166
|
+
protection `On` that read guards the append, under `Off` (the default) it is a
|
|
167
|
+
hint, and in an `[Unprotected]` command it is advisory. A state-dependent
|
|
168
|
+
permission or ownership rule (this member owns the order) may sit there too, with
|
|
169
|
+
the same limit. Under `[ProtectedDecision]` without `On`, only a parameterless
|
|
170
|
+
validator runs; see the command-validation and Chronicle guidance.
|
|
167
171
|
|
|
168
172
|
Read [authorization](references/authorization.md) for the guest rules, evaluation
|
|
169
173
|
order, the two baselines, default access under Arc's two hosting models, the
|
|
@@ -250,8 +254,9 @@ To run locally with an identity, supply the forwarded headers yourself — see
|
|
|
250
254
|
method directly.
|
|
251
255
|
- Observable queries that serve private or revocable data have an emission guard
|
|
252
256
|
backed by current session and membership state.
|
|
253
|
-
-
|
|
254
|
-
|
|
257
|
+
- State-dependent permission and ownership rules are guarded: read protection `On`
|
|
258
|
+
with the read model in the validator or handler, or `[ProtectedDecision]` commands
|
|
259
|
+
with the rule in `Provide()`/`Handle()`. Under `Off` they are hints.
|
|
255
260
|
- Discovery endpoints (`/.cratis/commands`, `/.cratis/queries`,
|
|
256
261
|
`/.cratis/identity-details/schema`, `/.cratis/users`, `/.cratis/tenants`) are
|
|
257
262
|
tested under Production settings; introspection is restricted or disabled where it
|
|
@@ -319,17 +319,17 @@ privileged.
|
|
|
319
319
|
|
|
320
320
|
## Protected decisions and validators
|
|
321
321
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
`[Unprotected]` command
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
322
|
+
Under read protection `On`, the read models in a convention-registered
|
|
323
|
+
`CommandValidator<T>` constructor are read fresh and guard the command's append, so
|
|
324
|
+
a state-dependent permission or ownership rule written there is decision-safe. Under
|
|
325
|
+
`Off` (the default) and `Measure` the same validator reads a plain model: a hint. In
|
|
326
|
+
an `[Unprotected]` command (or a model or method marked `[Unprotected("why")]`) the
|
|
327
|
+
read is advisory, so the rule is **not decision-safe**. A `[ProtectedDecision]`
|
|
328
|
+
command without `On` runs only certified, parameterless validators and refuses a
|
|
329
|
+
validator whose constructor takes dependencies; put such a rule in `Provide()` or
|
|
330
|
+
`Handle()` using `DecisionRead<T>` or `IDecisionReads`. Hand-registered scoped,
|
|
331
|
+
singleton, factory or instance validators are refused under `On`. Keep
|
|
332
|
+
static/service-locator state out of validators: a guard covers reads Arc can see.
|
|
333
333
|
Source: [protected validator boundary](https://github.com/Cratis/Arc/blob/main/Documentation/backend/csharp/chronicle/read-models/injecting-into-commands.md).
|
|
334
334
|
|
|
335
335
|
## Analyzer diagnostics
|
|
@@ -175,8 +175,9 @@ silently takes the first declared. Implement `ICanProvideEventSourceId`.
|
|
|
175
175
|
|
|
176
176
|
Read [decisions and routing](references/decisions-and-routing.md) when a command
|
|
177
177
|
needs guarded state, stream-id templates, definition-backed routes, event tags or
|
|
178
|
-
post-commit `CompleteStream`.
|
|
179
|
-
|
|
178
|
+
post-commit `CompleteStream`. A read-model parameter guards the append only under
|
|
179
|
+
read protection `On`; under `Off` (the default) it is advisory. `Result<,>` and
|
|
180
|
+
`concurrency: true` are not protection; see
|
|
180
181
|
[choosing how a rule holds](../cratis-chronicle-decision-reads/references/choosing-how-a-rule-holds.md).
|
|
181
182
|
A returned `EventForEventSourceId` route wins over command context.
|
|
182
183
|
|