@cratis/pi 7.125.2 → 7.127.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/package/corpus/agents/spec-writer.md +2 -2
  2. package/package/corpus/harnesses/copilot/instructions/application-profile.instructions.md +1 -1
  3. package/package/corpus/harnesses/copilot/instructions/fast-feedback.instructions.md +37 -0
  4. package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +3 -2
  5. package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +1 -1
  6. package/package/corpus/harnesses/copilot/instructions/specs.csharp.instructions.md +1 -1
  7. package/package/corpus/harnesses/copilot/instructions/specs.instructions.md +7 -1
  8. package/package/corpus/harnesses/copilot/instructions/specs.scenarios.csharp.instructions.md +40 -3
  9. package/package/corpus/harnesses/copilot/instructions/vertical-slices.instructions.md +1 -1
  10. package/package/corpus/harnesses/pi/agents/spec-writer.md +2 -2
  11. package/package/corpus/hooks/README.md +1 -1
  12. package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +13 -2
  13. package/package/corpus/rules/application-profile.md +1 -1
  14. package/package/corpus/rules/fast-feedback.md +36 -0
  15. package/package/corpus/rules/general.md +1 -0
  16. package/package/corpus/rules/profiles.md +3 -2
  17. package/package/corpus/rules/secrets.md +1 -1
  18. package/package/corpus/rules/specs.csharp.md +1 -1
  19. package/package/corpus/rules/specs.md +7 -1
  20. package/package/corpus/rules/specs.scenarios.csharp.md +40 -3
  21. package/package/corpus/rules/vertical-slices.md +1 -1
  22. package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +15 -10
  23. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +1 -1
  24. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authproxy.md +26 -0
  25. package/package/corpus/skills/cratis-arc-command/SKILL.md +7 -4
  26. package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +5 -0
  27. package/package/corpus/skills/cratis-chronicle-reactor/references/specifications.md +23 -2
  28. package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +6 -3
  29. package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +2 -2
  30. package/package/corpus/skills/cratis-code-review/SKILL.md +4 -2
  31. package/package/corpus/skills/cratis-environments-and-secrets/LICENSE +9 -0
  32. package/package/corpus/skills/cratis-environments-and-secrets/SKILL.md +112 -0
  33. package/package/corpus/skills/cratis-environments-and-secrets/references/environment-file.md +48 -0
  34. package/package/corpus/skills/cratis-onboarding-runtime/LICENSE +9 -0
  35. package/package/corpus/skills/cratis-onboarding-runtime/SKILL.md +94 -0
  36. package/package/corpus/skills/cratis-onboarding-runtime/references/ante-onboarding-keys.md +31 -0
  37. package/package/corpus/skills/cratis-onboarding-runtime/references/performance.md +86 -0
  38. package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/LICENSE +9 -0
  39. package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/SKILL.md +111 -0
  40. package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/references/onboarding-directives.md +89 -0
  41. package/package/corpus/skills/cratis-screenplay-authentication-and-onboarding/verification.json +32 -0
  42. package/package/corpus/skills/cratis-screenplay-command-surface/references/policy-persona-seed.md +1 -0
  43. package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md +1 -1
  44. package/package/corpus/skills/cratis-screenplay-model-review/SKILL.md +2 -0
  45. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/renderability-gate.md +8 -0
  46. package/package/corpus/skills/cratis-screenplay-toolchain/references/neighbouring-skills.md +2 -0
  47. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract-exceptions.json +70 -0
  48. package/package/corpus/skills/cratis-showrunner-herald/LICENSE +9 -0
  49. package/package/corpus/skills/cratis-showrunner-herald/SKILL.md +106 -0
  50. package/package/corpus/skills/cratis-showrunner-herald/references/options.md +35 -0
  51. package/package/corpus/skills/cratis-showrunner-herald/references/templates-and-images.md +70 -0
  52. package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +2 -2
  53. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +5 -0
  54. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/rendered-application.md +39 -0
  55. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/sandbox-and-specrunner.md +7 -0
  56. package/package/profile-catalog.json +6 -2
  57. package/package.json +1 -1
@@ -46,13 +46,13 @@ Select from these canonical rules in `.cratis/ai/rules/` only after applying the
46
46
 
47
47
  ## C# specs — start with the plain call
48
48
 
49
- Specify pure `Handle()`, validator, reducer and reactor decisions with plain calls and supplied read-model state. Follow `.cratis/ai/rules/specs.scenarios.csharp.md`: scenarios add only pipeline/authorization wiring, constraints, declarative mapping or application-owned race-policy proof, not a platform guard matrix. Keep the single cheap assembly-level admission spec for the application's own decision-model shapes. Prefer in-process helpers over out-of-process Chronicle hosts for the applicable behaviors:
49
+ Specify pure `Handle()`, validator, reducer and reactor decisions with plain calls and supplied read-model state. Follow `.cratis/ai/rules/specs.scenarios.csharp.md`: add exactly the scenarios its per-artifact decision table obligates (pipeline and authorization, constraints, projection and reducer dispatch, reactor dispatch and routing) plus application-owned race-policy proof, not a platform guard matrix. Keep the single cheap assembly-level admission spec for the application's own decision-model shapes. Prefer in-process helpers over out-of-process Chronicle hosts for the applicable behaviors:
50
50
 
51
51
  | Tool | Use for |
52
52
  |---|---|
53
53
  | `CommandScenario<TCommand>` | **State Change** — runs authorization + validators + `Provide()` + `Handle()` + appended events |
54
54
  | `EventScenario` | constraint violations, raw append/sequencing semantics |
55
- | `ReadModelScenario<TReadModel>` | **State View** — one class per read model, many read-only assertions on shared setup |
55
+ | `ReadModelScenario<TReadModel>` | **State View** — one class per read model by default (another only for a sequence that cannot share it), many read-only assertions on shared setup |
56
56
  | `ReactorScenario<TReactor>` | **Automation / Translation** — reactor invocation + side effects |
57
57
 
58
58
  Reserve out-of-process integration specs for host/transport/infra boundaries the scenario helpers can't exercise.
@@ -74,7 +74,7 @@ Tagged **[contract]** (framework-enforced) or **[convention]** (house default).
74
74
  3. **Code level** — infrastructure (hosting, DI composition, identity wiring, CI, deployment), clients, Screenplay code attachments and handlers, adapters in the render's customization area, and scope Stage cannot render yet (the model remains the contract). Framework repositories and brownfield work that is not opted in also stay code-first. Only the entry-point session may propose a model, at most once per session and never for trivial, bug-fix, infrastructure, client, framework or brownfield-maintenance work; if declined, do not ask again. Confirm Module/Feature, slice name, slice type and domain requirements as before; for new behavior or unclear event vocabulary run **cratis-chronicle-event-modeling** first, and record the agreed outline (fields, events, rules, scenarios) where the team tracks work, if anywhere. If you write code first where a model exists, say the model now lags.
75
75
  4. **Trivial changes** keep the proportional-delegation policy; Phases 1–3 below apply to code you write at either level.
76
76
  - **Phase 1 — Backend.** Implement a coherent slice change. **Gate:** incremental Debug build of the affected project (regenerates proxies, compiles `#if DEBUG` spec code).
77
- - **Phase 2 — Specs.** Mandatory for every slice type. Start with plain calls for pure command/validator/reducer decisions with supplied read-model state; add scenarios only for pipeline/authorization wiring, constraints or declarative mapping. Follow [specs.scenarios.csharp.md](./specs.scenarios.csharp.instructions.md) for shared setup and cost budgets. **Gate:** tests pass.
77
+ - **Phase 2 — Specs.** Mandatory for every slice type. Start with plain calls for pure command/validator/reducer decisions with supplied read-model state; add the scenarios the decision table in [specs.scenarios.csharp.md](./specs.scenarios.csharp.instructions.md) obligates, and follow it for shared setup and cost budgets. **Gate:** tests pass.
78
78
  - **Phase 3 — Frontend.** Build from the generated proxies, register in the composition page, wire routing. **Gate:** lint, conditional test, build.
79
79
 
80
80
  **Backend before frontend, always** — the frontend depends on proxies that exist only after a successful build. Generate proxies from the **Debug** build (house convention); verify Release without re-running the generator: `dotnet build -c Release -p:CratisProxiesOutputPath=`. Why that property, and what to do when nothing is generated: the **cratis-arc-command** skill's proxy-generation reference.
@@ -0,0 +1,37 @@
1
+ ---
2
+ applyTo: "**/for_*/**,**/when_*/**,**/*.csproj,**/Directory.Build.props,**/Directory.Build.targets,**/*.Specs/**,**/.github/workflows/**"
3
+ paths:
4
+ - "**/for_*/**"
5
+ - "**/when_*/**"
6
+ - "**/*.csproj"
7
+ - "**/Directory.Build.props"
8
+ - "**/Directory.Build.targets"
9
+ - "**/*.Specs/**"
10
+ - "**/.github/workflows/**"
11
+ ---
12
+ <!-- cratis-ai: generated Copilot instructions. Do not edit; rules/ is the source. -->
13
+
14
+ # Fast feedback is a product requirement
15
+
16
+ Applies to every Cratis repository and every application built on Cratis. A build and a spec run are
17
+ measured in seconds. Each line is tagged **[contract]** (binding) or **[convention]** (the house
18
+ default) per [`general.md`](../../../rules/general.md).
19
+
20
+ - **[contract] Building and running specs is as fast as possible, locally and in CI.** A change
21
+ that makes a build or a spec run slower is a regression: measure it (cold full-suite wall time
22
+ plus a focused single-folder loop) and fix it in the same change.
23
+ - **[contract] Every spec is safe to run in parallel with every other spec.** No shared mutable
24
+ static state, no ordering assumptions, no fixed ports, paths or names that two specs could both
25
+ claim. Never serialize a suite or opt out of shared setup to hide an isolation bug.
26
+ - **[contract] No per-spec startup cost for convenience.** Setup is built once and shared
27
+ (shared lifecycle stays on). Slow setup in a framework harness is fixed in the framework and
28
+ reported upstream; do not work around it by merging unrelated assertions or skipping specs.
29
+ - **[contract] Analyzers and generators are cheap or they are not added.** A slow one is an
30
+ upstream issue; see [`framework.md`](./framework.instructions.md) for how analyzers are written for speed.
31
+ - **[convention] Specify with the cheapest proof that is real.** Call a pure decision directly;
32
+ use a scenario for what only the framework pipeline or engine proves. The per-artifact table is in
33
+ [`specs.scenarios.csharp.md`](./specs.scenarios.csharp.instructions.md); the universal base is in
34
+ [`specs.md`](./specs.instructions.md).
35
+
36
+ A cold full-suite run under a minute is the target for a repository's spec gate. It is a budget to
37
+ protect, not a reason to delete coverage.
@@ -215,12 +215,12 @@ language policy always follows the consuming repository's `go.mod` minimum.
215
215
  | `cratis/codescene` | Opt-in CodeScene Code Health change gates, refactoring, debt prioritization and risk-based testing; requires an account and available MCP tools; each user registers the MCP server (see below) |
216
216
  | `cratis/playwright` | Opt-in Playwright browser automation MCP server declaration for exploratory checks; carries no skills or rules and each user registers the server (see below) |
217
217
  | `cratis/studio` | Studio MCP safety guidance |
218
- | `cratis/cli` | CLI operations |
218
+ | `cratis/cli` | CLI operations, project environments and secrets |
219
219
  | `cratis/cli/terminal-workbench` | Human-operated terminal Workbench |
220
220
  | `cratis/chronicle/mcp` | Chronicle MCP inspection and exact tool-effect guidance |
221
221
  | `cratis/lens` | Lens browser extension |
222
222
  | `cratis/screenplay` | Model-first event modeling: lifecycle, phase skills, toolchain, review, render and gap-fill, plus the whole `.play` language |
223
- | `cratis/stage` | Stage rendering and sandbox |
223
+ | `cratis/stage` | Stage rendering and sandbox, onboarding runtime (Ante, Stage Manager) |
224
224
  | `cratis/modeling/screenplay-stage` | Screenplay + Stage together |
225
225
 
226
226
  ### Code Health with CodeScene
@@ -302,6 +302,7 @@ and one phase skill, then the construct skill needed for syntax.
302
302
  | `cratis-screenplay-read-surface` | `readmodel`, `query`, `screen`, name resolution |
303
303
  | `cratis-screenplay-ui-composition` | `layout`, templates, `form`, `contribute`, `ui profile`, `theme`, `$strings`, `file` |
304
304
  | `cratis-screenplay-captures-and-reactions` | The Change Data Capture Language, `reaction`, `trigger` |
305
+ | `cratis-screenplay-authentication-and-onboarding` | `authentication`, `onboarding`, and the environment keys derived from them |
305
306
  | `cratis-screenplay-specifications` | Given/when/then and the reference execution |
306
307
  | `cratis-screenplay-model-authoring` | Typed MCP authoring, model navigation/refactoring, compiler diagnostics, and source-versus-executable readiness |
307
308
 
@@ -17,7 +17,7 @@ Every line is tagged **[contract]** (binding) per the Three Levels of Authority
17
17
  secrets; `kubectl get secret`; `kubectl config view --raw`; `env`, `printenv`, `export -p`
18
18
  (including `env | …`); `docker inspect`; `gh secret`, `gh api …/secrets`, `gh auth token`;
19
19
  `vault read`, `vault kv get`; cloud secret-manager reads; `pulumi … --show-secrets`;
20
- reading kubeconfigs, `.env*` files, private keys or token files.
20
+ `cratis config get … --show-secret`; reading kubeconfigs, `.env*` files, private keys or token files.
21
21
  - **[contract] Project the field you need and say which one:** `-o jsonpath='{.spec.containers[*].image}'`,
22
22
  `-o custom-columns=…`, `kubectl get … -o name`, status fields, `kubectl logs`, `docker inspect --format`.
23
23
  Read a `.env.example`, never the `.env`.
@@ -12,7 +12,7 @@ Extends the base [specs.md](./specs.instructions.md) (folder structure, naming,
12
12
 
13
13
  > **Which spec surface?**
14
14
  > - **Framework / library code** (Chronicle kernel, Arc pipeline, source generators, Fundamentals): the `Specification` base + NSubstitute in this file is the dominant mode — unit-test the classes under test in isolation. Reach for a `*Scenario` helper (in [specs.scenarios.csharp.md](./specs.scenarios.csharp.instructions.md)) **only** when testing the very engine your repo provides — Arc → `CommandScenario`, Chronicle → the event/projection/reactor scenarios. The **cratis-application-slice-specifications** skill is application-oriented, not for general framework specs.
15
- > - **Event-sourced applications on Cratis** (commands, projections, reducers, reactors, constraints): use this base plus a direct call by default for pure `Handle()`, validator and reducer decisions with supplied read-model state; add scenarios only for pipeline/authorization wiring, constraints or declarative projections — see **[specs.scenarios.csharp.md](./specs.scenarios.csharp.instructions.md)** (`profile: application`) and the **cratis-application-slice-specifications** skill.
15
+ > - **Event-sourced applications on Cratis** (commands, projections, reducers, reactors, constraints): use this base plus a direct call by default for pure `Handle()`, validator and reducer decisions with supplied read-model state; add the scenarios its per-artifact decision table obligates — see **[specs.scenarios.csharp.md](./specs.scenarios.csharp.instructions.md)** (`profile: application`) and the **cratis-application-slice-specifications** skill.
16
16
 
17
17
  `Cratis.Specifications` keeps the approach of Machine.Specifications (MSpec): `Establish → Because → should_` maps to "Given → When → Then" and keeps each spec focused on *one setup, one action, one set of assertions*.
18
18
 
@@ -98,13 +98,19 @@ The goal is to cover *decisions and transformations* — code where bugs hide. S
98
98
 
99
99
  **Avoid file names starting with:** `when_getting_*`, `when_returning_*` — if a spec name starts with "getting" or "returning", it's probably testing a simple getter, which is not worth specifying.
100
100
 
101
+ ## Choosing the harness
102
+
103
+ Call the code. A function whose result depends only on its arguments is specified by calling it with those arguments and asserting the result: no host, container or harness. Use a harness (scenario, fixture, in-memory engine) for behavior that exists only in the framework: discovery, attribute- or convention-driven wiring, authorization, persistence-time rules and declarative mappings. Name each such spec after the wiring it proves, and do not repeat through the harness a decision a plain call already proves.
104
+
105
+ Substitute collaborators (a service, a clock, a sender) freely in a plain call. Never substitute the event store, a read-model store or the pipeline as proof of persistence, projection or pipeline behavior: either make the code a pure function of its inputs, or keep the harness. Harness coverage is an obligation per behavior the framework owns, not a quota. Fast, parallel-safe harnesses are required by [fast-feedback.md](./fast-feedback.instructions.md).
106
+
101
107
  ## Multiple Outcomes
102
108
 
103
109
  Each distinct outcome deserves its own spec file. This keeps specs small, focused, and independently verifiable. When a spec fails, you immediately know *which* outcome broke — no debugging through a multi-assertion file.
104
110
 
105
111
  - When a behavior has multiple outcomes, create a `when_<behavior>/` folder with separate files for each outcome.
106
112
  - For simple behaviors with a single outcome, use a single file: `when_<behavior>`.
107
- - Never write a single file that tests an entire class's unrelated behaviors. **C# declarative read-model mappings are the exception:** use one scenario class per read model, one shared event sequence with captured intermediate states, and many read-only facts for fields and lifecycle outcomes. See [specs.scenarios.csharp.md](./specs.scenarios.csharp.instructions.md) for scenario selection and cost budgets.
113
+ - Never write a single file that tests an entire class's unrelated behaviors. **C# declarative read-model mappings are the exception:** use one scenario class per read model by default (another only for a sequence that cannot share it, such as a removal or another key), one shared event sequence with captured intermediate states, and many read-only facts for fields and lifecycle outcomes. See [specs.scenarios.csharp.md](./specs.scenarios.csharp.instructions.md) for scenario selection and cost budgets.
108
114
 
109
115
  ## Reusable Context
110
116
 
@@ -15,7 +15,44 @@ Specs are **mandatory for every slice type**, including reactors.
15
15
 
16
16
  ## Start with the plain call; add a scenario where it adds proof
17
17
 
18
- **Judgment-shaped convention:** call pure `Handle()`, validators, reducers and reactor decisions directly with their inputs, including read-model state. Add a scenario only for pipeline/authorization wiring, constraints or declarative projection mapping. Do not repeat every decision through the pipeline; name the extra behavior each scenario proves.
18
+ Call pure `Handle()`, validators, reducers and reactor decisions directly with their inputs, including read-model state. Add a scenario only for the wiring the decision table below obligates (pipeline and authorization, constraints, projection and reducer dispatch, reactor dispatch and routing). Do not repeat every decision through the pipeline; name the extra behavior each scenario proves.
19
+
20
+ **Decision table — apply it per artifact, without judgment.** A plain call proves the decision; a scenario is a coverage obligation for the wiring only the framework proves. These are obligations, not quotas: add a scenario for each behavior listed, and none beyond it.
21
+
22
+ | Artifact | Plain call (`Specification`) | Scenario obligation |
23
+ |---|---|---|
24
+ | Command with a pure `Handle(…)` | every outcome and rejection branch, with read-model state supplied | the pipeline path once (`CommandScenario`: discovery, DI, event source, appended events), plus a **negative proof for each wiring that can refuse before `Handle`**: authorization denies (`ShouldNotBeAuthorized`), validation rejects, `Provide()` short-circuits |
25
+ | `Handle` that needs `IEventStore`, `IReadModels` or services | none: do not substitute the store | keep the scenario; when touched, take the read model as a `Handle` parameter and use the row above |
26
+ | `CommandValidator`, `ConceptValidator`, `Provide()` logic | each rule, built with its state | the command's pipeline scenarios prove the wiring |
27
+ | Constraint (`[Unique]`, `IConstraint`) | none | an `EventScenario` per constraint: the violation and the nearest non-violation |
28
+ | Projection or read model | none | `ReadModelScenario` against the real engine, covering **each event, key, join, children and removal the projection declares**: one class per read model with one shared event sequence by default; add a class only for a sequence that cannot share it (a removal, another key) |
29
+ | Reducer | each transition: `Model.Reduce(event, current[, context])` | a `ReadModelScenario` for dispatch, filters and stream routing |
30
+ | Reactor | each handler decision and side effect, called directly with substitute collaborators | a `ReactorScenario`: dispatch, filtering, read-model parameters, routing of produced events to the right event source, `[OnceOnly]`/replay |
31
+ | Query | extracted filter, sort or composition logic | `QueryScenario` for arguments, authorization and paging; provider behavior is integration |
32
+ | Cross-slice flow | each link | out-of-process integration, outside the fast loop |
33
+
34
+ Name a scenario after the wiring it proves (`and_the_caller_is_not_a_maintainer`). A plain `Handle()` spec never takes a validator, authorization or constraint name: it did not run them. Merge scenario classes only with an assertion map that keeps every assertion. Never use a substitute for the event store or a read-model store as proof of persistence or projection. Cost and parallel-safety requirements are in [fast-feedback.md](./fast-feedback.instructions.md).
35
+
36
+ #### Negative authorization proof
37
+
38
+ ```csharp
39
+ #if DEBUG
40
+ namespace MyApp.Authors.Registration.when_registering_an_author;
41
+
42
+ public class and_the_caller_is_not_a_maintainer : Specification
43
+ {
44
+ readonly CommandScenario<RegisterAuthor> _scenario = new(); // no identity registered
45
+ CommandResult _result;
46
+
47
+ async Task Because() => _result = await _scenario.Execute(new RegisterAuthor(AuthorId.New(), "Jane Austen"));
48
+
49
+ [Fact] void should_be_denied() => _result.ShouldNotBeAuthorized();
50
+ [Fact] async Task should_append_nothing() => await _scenario.ShouldNotHaveAppendedEvents();
51
+ }
52
+ #endif
53
+ ```
54
+
55
+ An unauthenticated scenario against an `[Authorize]`/`[Roles]` command is denied before `Handle`; pair it with the happy-path scenario that registers an allowed identity in `_scenario.Services`.
19
56
 
20
57
  **Application specs supply state, not decision-read infrastructure.** Call `Handle()` or the validator directly with supplied read-model state. Never call `UseDecisionReads`, configure protection modes, seed events solely for infrastructure, or re-prove the platform's concurrency, conflict-refusal or isolation matrix; Arc/Chronicle specs own it. The application's **only decision-read spec obligation** is one cheap assembly-level shape-admission spec: `DecisionReadAdmissions.ShouldAdmitDecisionReadsIn(assembly)` (`Cratis.Arc.Chronicle.Testing.Commands`). It checks the application's own models/projections without executing commands or running Chronicle, not the platform guards. Never simulate competing appends either (`AppendConcurrently` or any hand-built interleaving): concurrency is a framework concern a developer should not think about in tests, and Arc and Chronicle specs prove it. A rule that must hold under races is expressed in production by taking the read model as a `Handle` parameter under `[ReadProtection(On)]`; the spec gives that read model's state and asserts the outcome. Report harness state gaps upstream; never weaken production guards for speed. `AppendConcurrently` is an advanced framework-verification API: use only for an explicit need. Direction, no release claim: Arc will obsolete `UseDecisionReads`.
21
58
 
@@ -27,7 +64,7 @@ Specs are **mandatory for every slice type**, including reactors.
27
64
  | `CommandScenario<TCommand>` | the real Arc command pipeline — authorization + validators + `Provide()` + `Handle()` + appended events | pipeline wiring, authorization or constraints adds proof |
28
65
  | `QueryScenario<TReadModel>` | query pipeline: argument conversion, authorization, validation, interceptors; `Observe` buffers emissions | query composition matters; real MongoDB observations still need infrastructure |
29
66
  | `EventScenario` | Chronicle-level append semantics, no command pipeline | constraint violations, raw stream sequencing/concurrency |
30
- | `ReadModelScenario<TReadModel>` | projection/reducer state from a sequence of events (auto-detects model-bound / fluent / reducer) | one class per read model, many read-only assertions on shared setup |
67
+ | `ReadModelScenario<TReadModel>` | projection/reducer state from a sequence of events (auto-detects model-bound / fluent / reducer) | one class per read model by default, many read-only assertions on shared setup (see the decision table for when to add another) |
31
68
  | `ReactorScenario<TReactor>` | reactor handler invocation + side effects via mocked services | the reactor wiring and side effects (Automation / Translation slices) |
32
69
 
33
70
  > **Out-of-process Chronicle integration specs are an advanced case** — reserve them for host wiring, real infrastructure, serialization/transport, or end-to-end boundaries the scenario helpers can't exercise. They are not the default vertical-slice test shape.
@@ -144,7 +181,7 @@ async Task Because() =>
144
181
 
145
182
  ### `ReadModelScenario<TReadModel>` — declarative projections and reducer wiring
146
183
 
147
- The default for declarative projections; pure reducer decisions start with a direct call. Use **one scenario class per read model**, not one per property, mapping attribute or lifecycle event. Apply its event sequence once, capture intermediate states in `Because` where needed, then assert fields, children, counters and lifecycle outcomes in many read-only facts. Add a reducer scenario only for dispatch, filtering, metadata or read-model wiring. Use xUnit `Assert.*` on `_scenario.Instance`.
184
+ The default for declarative projections; pure reducer decisions start with a direct call. Use **one scenario class per read model**, not one per property, mapping attribute or lifecycle event; add another class only for an event sequence that cannot share the first (a removal, another key), as the decision table says. Apply its event sequence once, capture intermediate states in `Because` where needed, then assert fields, children, counters and lifecycle outcomes in many read-only facts. Add a reducer scenario only for dispatch, filtering, metadata or read-model wiring. Use xUnit `Assert.*` on `_scenario.Instance`.
148
185
 
149
186
  ```csharp
150
187
  ReadModelScenario<Author> _scenario = null!;
@@ -21,7 +21,7 @@ This file is the reference for *what* goes in each part of a slice. Layout, slic
21
21
 
22
22
  - .NET / C# (ASP.NET Core) — **Cratis Arc** for CQRS / model-bound commands and queries, **Cratis Chronicle** for event sourcing, MongoDB or EF Core for read models.
23
23
  - React + TypeScript (Vite) — Cratis Components 4.x + Arc-generated proxies, MVVM. Vitest + Mocha/Chai/Sinon for frontend specs.
24
- - xUnit + Cratis.Specifications + NSubstitute for C# specs (`Specification` plus a direct call for pure command/validator/reducer decisions with supplied state; add scenarios only for pipeline/authorization wiring, constraints or declarative projections — see [specs.md](./specs.instructions.md)).
24
+ - xUnit + Cratis.Specifications + NSubstitute for C# specs (`Specification` plus a direct call for pure command/validator/reducer decisions with supplied state; add the scenarios the per-artifact decision table in [specs.scenarios.csharp.md](./specs.scenarios.csharp.instructions.md) obligates).
25
25
 
26
26
  ## Proxy generation — the build dependency
27
27
 
@@ -41,13 +41,13 @@ Select from these canonical rules in `.cratis/ai/rules/` only after applying the
41
41
 
42
42
  ## C# specs — start with the plain call
43
43
 
44
- Specify pure `Handle()`, validator, reducer and reactor decisions with plain calls and supplied read-model state. Follow `.cratis/ai/rules/specs.scenarios.csharp.md`: scenarios add only pipeline/authorization wiring, constraints, declarative mapping or application-owned race-policy proof, not a platform guard matrix. Keep the single cheap assembly-level admission spec for the application's own decision-model shapes. Prefer in-process helpers over out-of-process Chronicle hosts for the applicable behaviors:
44
+ Specify pure `Handle()`, validator, reducer and reactor decisions with plain calls and supplied read-model state. Follow `.cratis/ai/rules/specs.scenarios.csharp.md`: add exactly the scenarios its per-artifact decision table obligates (pipeline and authorization, constraints, projection and reducer dispatch, reactor dispatch and routing) plus application-owned race-policy proof, not a platform guard matrix. Keep the single cheap assembly-level admission spec for the application's own decision-model shapes. Prefer in-process helpers over out-of-process Chronicle hosts for the applicable behaviors:
45
45
 
46
46
  | Tool | Use for |
47
47
  |---|---|
48
48
  | `CommandScenario<TCommand>` | **State Change** — runs authorization + validators + `Provide()` + `Handle()` + appended events |
49
49
  | `EventScenario` | constraint violations, raw append/sequencing semantics |
50
- | `ReadModelScenario<TReadModel>` | **State View** — one class per read model, many read-only assertions on shared setup |
50
+ | `ReadModelScenario<TReadModel>` | **State View** — one class per read model by default (another only for a sequence that cannot share it), many read-only assertions on shared setup |
51
51
  | `ReactorScenario<TReactor>` | **Automation / Translation** — reactor invocation + side effects |
52
52
 
53
53
  Reserve out-of-process integration specs for host/transport/infra boundaries the scenario helpers can't exercise.
@@ -11,7 +11,7 @@ Three layers:
11
11
  | Pattern pass | `PostToolUse` on a write | `scripts/cratis-pattern-scan.sh` | zero tokens until a match | appends a one-line reminder to context, never blocks |
12
12
  | Hard block | `PreToolUse` on a write | `scripts/cratis-guard-writes.sh` | zero | exits **2** — the write does not happen |
13
13
  | Hard block | `PreToolUse` on `Bash` | `scripts/cratis-guard-store-mutations.sh` | zero; parses only commands that mention `cratis` | exits **2** — the store-changing `cratis chronicle` command does not run |
14
- | Hard block | `PreToolUse` on `Bash` | `scripts/cratis-guard-secrets.sh` | zero; parses only commands that mention a credential-bearing tool or file | exits **2** — a command that prints credentials (full `kubectl` specs and secrets, `env`/`printenv`, `docker inspect`, secret-manager reads, `.env`/kubeconfig/key files; also behind `/usr/bin/env`, `command`, attached options such as `-ePATTERN`, a bare `{.}` projection) does not run unless it projects a field; piped shells and recursive searches with no named file stay uncovered; see [`secrets.md`](../rules/secrets.md) |
14
+ | Hard block | `PreToolUse` on `Bash` | `scripts/cratis-guard-secrets.sh` | zero; parses only commands that mention a credential-bearing tool or file | exits **2** — a command that prints credentials (full `kubectl` specs and secrets, `env`/`printenv`, `docker inspect`, secret-manager reads, `cratis config get --show-secret`, `.env`/kubeconfig/key files; also behind `/usr/bin/env`, `command`, attached options such as `-ePATTERN`, a bare `{.}` projection) does not run unless it projects a field; piped shells and recursive searches with no named file stay uncovered; see [`secrets.md`](../rules/secrets.md) |
15
15
  | Hard block | `PreToolUse` on `Bash` | `scripts/cratis-guard-pr-body.sh` | fetches shared rules only for `gh pr create/edit` | exits **2** for inline bodies, invalid notes or conflicting release labels; warns and allows when offline with no cache |
16
16
  | Cleanup of delivered work | `PostToolUse` on `Bash` (`gh pr merge`, `git pull/merge/worktree`) and `SessionStart` (Pi: `tool_result` and `session_start`) | `scripts/cratis-cleanup-worktrees.sh --hook` | zero when the command is unrelated | never blocks; removes delivered worktrees and branches, reports what it kept |
17
17
  | Quality gate (Claude) | `Stop` | `scripts/cratis-quality-gate.sh` | one build/test run, only when relevant files changed | exits **2** — the turn does not end |
@@ -35,7 +35,7 @@ command_text="$(hook_json "$input" '.tool_input.command')"
35
35
 
36
36
  # Cheap exit for the overwhelming majority of shell commands, which mention none of these words.
37
37
  case "$(printf '%s' "$command_text" | tr '[:upper:]' '[:lower:]')" in
38
- *kubectl*|*printenv*|*env*|*docker*|*podman*|*"gh "*|*vault*|*pulumi*|*aws*|*"az "*|*gcloud*|*kubeconfig*|*.kube*|\
38
+ *kubectl*|*printenv*|*env*|*docker*|*podman*|*"gh "*|*vault*|*pulumi*|*show-secret*|*aws*|*"az "*|*gcloud*|*kubeconfig*|*.kube*|\
39
39
  *export*|*declare*|*typeset*|*id_rsa*|*id_ed25519*|*id_ecdsa*|*id_dsa*|*.pem*|*.p12*|*.pfx*|*token*|*.netrc*|*.npmrc*|\
40
40
  *credentials*|*.pgpass*|*secrets*) ;;
41
41
  *) exit 0 ;;
@@ -397,7 +397,7 @@ function drop_redirects(A, s, n, k, m, w) {
397
397
  }
398
398
  return m
399
399
  }
400
- function analyze(A, s, n, w, b, k, names, p) {
400
+ function analyze(A, s, n, w, b, k, names, p, config, get, show) {
401
401
  while (s <= n) {
402
402
  w = A[s]; b = base(w)
403
403
  if (is_assign(w)) { s++; continue }
@@ -446,6 +446,17 @@ function analyze(A, s, n, w, b, k, names, p) {
446
446
  for (k = s + 1; k <= n; k++) if (A[k] ~ /^--show-secrets/) report("pulumi --show-secrets prints decrypted secrets", "pulumi config / pulumi stack output without --show-secrets (secrets stay masked)")
447
447
  return
448
448
  }
449
+ # `cratis config get` prints a secret as [secret]; --show-secret resolves it through the secrets provider.
450
+ if (b == "cratis") {
451
+ config = 0
452
+ for (k = s + 1; k <= n; k++) {
453
+ if (A[k] == "config") config = 1
454
+ else if (config && A[k] == "get") get = 1
455
+ if (A[k] ~ /^--show-secret/) show = 1
456
+ }
457
+ if (config && get && show) report("cratis config get --show-secret prints a decrypted secret", "cratis config get without --show-secret (a secret prints as [secret]); name the key in your report")
458
+ return
459
+ }
449
460
  # A redirect is not a variable name: `printenv > f` and `export > f` still dump the whole environment.
450
461
  if (b == "printenv" || b == "export" || b == "declare" || b == "typeset") n = drop_redirects(A, s, n)
451
462
  if (b == "printenv") {
@@ -73,7 +73,7 @@ Tagged **[contract]** (framework-enforced) or **[convention]** (house default).
73
73
  3. **Code level** — infrastructure (hosting, DI composition, identity wiring, CI, deployment), clients, Screenplay code attachments and handlers, adapters in the render's customization area, and scope Stage cannot render yet (the model remains the contract). Framework repositories and brownfield work that is not opted in also stay code-first. Only the entry-point session may propose a model, at most once per session and never for trivial, bug-fix, infrastructure, client, framework or brownfield-maintenance work; if declined, do not ask again. Confirm Module/Feature, slice name, slice type and domain requirements as before; for new behavior or unclear event vocabulary run **cratis-chronicle-event-modeling** first, and record the agreed outline (fields, events, rules, scenarios) where the team tracks work, if anywhere. If you write code first where a model exists, say the model now lags.
74
74
  4. **Trivial changes** keep the proportional-delegation policy; Phases 1–3 below apply to code you write at either level.
75
75
  - **Phase 1 — Backend.** Implement a coherent slice change. **Gate:** incremental Debug build of the affected project (regenerates proxies, compiles `#if DEBUG` spec code).
76
- - **Phase 2 — Specs.** Mandatory for every slice type. Start with plain calls for pure command/validator/reducer decisions with supplied read-model state; add scenarios only for pipeline/authorization wiring, constraints or declarative mapping. Follow [specs.scenarios.csharp.md](./specs.scenarios.csharp.md) for shared setup and cost budgets. **Gate:** tests pass.
76
+ - **Phase 2 — Specs.** Mandatory for every slice type. Start with plain calls for pure command/validator/reducer decisions with supplied read-model state; add the scenarios the decision table in [specs.scenarios.csharp.md](./specs.scenarios.csharp.md) obligates, and follow it for shared setup and cost budgets. **Gate:** tests pass.
77
77
  - **Phase 3 — Frontend.** Build from the generated proxies, register in the composition page, wire routing. **Gate:** lint, conditional test, build.
78
78
 
79
79
  **Backend before frontend, always** — the frontend depends on proxies that exist only after a successful build. Generate proxies from the **Debug** build (house convention); verify Release without re-running the generator: `dotnet build -c Release -p:CratisProxiesOutputPath=`. Why that property, and what to do when nothing is generated: the **cratis-arc-command** skill's proxy-generation reference.
@@ -0,0 +1,36 @@
1
+ ---
2
+ applyTo: "**/for_*/**,**/when_*/**,**/*.csproj,**/Directory.Build.props,**/Directory.Build.targets,**/*.Specs/**,**/.github/workflows/**"
3
+ paths:
4
+ - "**/for_*/**"
5
+ - "**/when_*/**"
6
+ - "**/*.csproj"
7
+ - "**/Directory.Build.props"
8
+ - "**/Directory.Build.targets"
9
+ - "**/*.Specs/**"
10
+ - "**/.github/workflows/**"
11
+ ---
12
+
13
+ # Fast feedback is a product requirement
14
+
15
+ Applies to every Cratis repository and every application built on Cratis. A build and a spec run are
16
+ measured in seconds. Each line is tagged **[contract]** (binding) or **[convention]** (the house
17
+ default) per [`general.md`](./general.md).
18
+
19
+ - **[contract] Building and running specs is as fast as possible, locally and in CI.** A change
20
+ that makes a build or a spec run slower is a regression: measure it (cold full-suite wall time
21
+ plus a focused single-folder loop) and fix it in the same change.
22
+ - **[contract] Every spec is safe to run in parallel with every other spec.** No shared mutable
23
+ static state, no ordering assumptions, no fixed ports, paths or names that two specs could both
24
+ claim. Never serialize a suite or opt out of shared setup to hide an isolation bug.
25
+ - **[contract] No per-spec startup cost for convenience.** Setup is built once and shared
26
+ (shared lifecycle stays on). Slow setup in a framework harness is fixed in the framework and
27
+ reported upstream; do not work around it by merging unrelated assertions or skipping specs.
28
+ - **[contract] Analyzers and generators are cheap or they are not added.** A slow one is an
29
+ upstream issue; see [`framework.md`](./framework.md) for how analyzers are written for speed.
30
+ - **[convention] Specify with the cheapest proof that is real.** Call a pure decision directly;
31
+ use a scenario for what only the framework pipeline or engine proves. The per-artifact table is in
32
+ [`specs.scenarios.csharp.md`](./specs.scenarios.csharp.md); the universal base is in
33
+ [`specs.md`](./specs.md).
34
+
35
+ A cold full-suite run under a minute is the target for a repository's spec gate. It is a budget to
36
+ protect, not a reason to delete coverage.
@@ -139,6 +139,7 @@ A claim is only as good as the signal behind it — a build result, a test run,
139
139
  | React + Arc + Cratis Components + MVVM + dialogs | `react.md`, `components.md`, `dialogs.md` |
140
140
  | Frontend engineering quality & testing | `frontend-quality.md`, `frontend-testing.md`, `storybook.md` |
141
141
  | Spec patterns — universal `Specification` base (both profiles) | `specs.md`, `specs.csharp.md`, `specs.typescript.md` |
142
+ | Build and spec-run speed: parallel-safe specs, no per-spec startup, cheap analyzers | `fast-feedback.md` |
142
143
  | Spec patterns — the four `*Scenario` helpers (application only) | `specs.scenarios.csharp.md` |
143
144
  | Strongly-typed values (`ConceptAs<T>`, `EventSourceId<T>`) | `concepts.md` |
144
145
  | Shared term definitions (event, projection, reducer, reactor, observer, DCB, …) | `glossary.md` |
@@ -214,12 +214,12 @@ language policy always follows the consuming repository's `go.mod` minimum.
214
214
  | `cratis/codescene` | Opt-in CodeScene Code Health change gates, refactoring, debt prioritization and risk-based testing; requires an account and available MCP tools; each user registers the MCP server (see below) |
215
215
  | `cratis/playwright` | Opt-in Playwright browser automation MCP server declaration for exploratory checks; carries no skills or rules and each user registers the server (see below) |
216
216
  | `cratis/studio` | Studio MCP safety guidance |
217
- | `cratis/cli` | CLI operations |
217
+ | `cratis/cli` | CLI operations, project environments and secrets |
218
218
  | `cratis/cli/terminal-workbench` | Human-operated terminal Workbench |
219
219
  | `cratis/chronicle/mcp` | Chronicle MCP inspection and exact tool-effect guidance |
220
220
  | `cratis/lens` | Lens browser extension |
221
221
  | `cratis/screenplay` | Model-first event modeling: lifecycle, phase skills, toolchain, review, render and gap-fill, plus the whole `.play` language |
222
- | `cratis/stage` | Stage rendering and sandbox |
222
+ | `cratis/stage` | Stage rendering and sandbox, onboarding runtime (Ante, Stage Manager) |
223
223
  | `cratis/modeling/screenplay-stage` | Screenplay + Stage together |
224
224
 
225
225
  ### Code Health with CodeScene
@@ -301,6 +301,7 @@ and one phase skill, then the construct skill needed for syntax.
301
301
  | `cratis-screenplay-read-surface` | `readmodel`, `query`, `screen`, name resolution |
302
302
  | `cratis-screenplay-ui-composition` | `layout`, templates, `form`, `contribute`, `ui profile`, `theme`, `$strings`, `file` |
303
303
  | `cratis-screenplay-captures-and-reactions` | The Change Data Capture Language, `reaction`, `trigger` |
304
+ | `cratis-screenplay-authentication-and-onboarding` | `authentication`, `onboarding`, and the environment keys derived from them |
304
305
  | `cratis-screenplay-specifications` | Given/when/then and the reference execution |
305
306
  | `cratis-screenplay-model-authoring` | Typed MCP authoring, model navigation/refactoring, compiler diagnostics, and source-versus-executable readiness |
306
307
 
@@ -16,7 +16,7 @@ Every line is tagged **[contract]** (binding) per the Three Levels of Authority
16
16
  secrets; `kubectl get secret`; `kubectl config view --raw`; `env`, `printenv`, `export -p`
17
17
  (including `env | …`); `docker inspect`; `gh secret`, `gh api …/secrets`, `gh auth token`;
18
18
  `vault read`, `vault kv get`; cloud secret-manager reads; `pulumi … --show-secrets`;
19
- reading kubeconfigs, `.env*` files, private keys or token files.
19
+ `cratis config get … --show-secret`; reading kubeconfigs, `.env*` files, private keys or token files.
20
20
  - **[contract] Project the field you need and say which one:** `-o jsonpath='{.spec.containers[*].image}'`,
21
21
  `-o custom-columns=…`, `kubectl get … -o name`, status fields, `kubectl logs`, `docker inspect --format`.
22
22
  Read a `.env.example`, never the `.env`.
@@ -11,7 +11,7 @@ Extends the base [specs.md](./specs.md) (folder structure, naming, philosophy) w
11
11
 
12
12
  > **Which spec surface?**
13
13
  > - **Framework / library code** (Chronicle kernel, Arc pipeline, source generators, Fundamentals): the `Specification` base + NSubstitute in this file is the dominant mode — unit-test the classes under test in isolation. Reach for a `*Scenario` helper (in [specs.scenarios.csharp.md](./specs.scenarios.csharp.md)) **only** when testing the very engine your repo provides — Arc → `CommandScenario`, Chronicle → the event/projection/reactor scenarios. The **cratis-application-slice-specifications** skill is application-oriented, not for general framework specs.
14
- > - **Event-sourced applications on Cratis** (commands, projections, reducers, reactors, constraints): use this base plus a direct call by default for pure `Handle()`, validator and reducer decisions with supplied read-model state; add scenarios only for pipeline/authorization wiring, constraints or declarative projections — see **[specs.scenarios.csharp.md](./specs.scenarios.csharp.md)** (`profile: application`) and the **cratis-application-slice-specifications** skill.
14
+ > - **Event-sourced applications on Cratis** (commands, projections, reducers, reactors, constraints): use this base plus a direct call by default for pure `Handle()`, validator and reducer decisions with supplied read-model state; add the scenarios its per-artifact decision table obligates — see **[specs.scenarios.csharp.md](./specs.scenarios.csharp.md)** (`profile: application`) and the **cratis-application-slice-specifications** skill.
15
15
 
16
16
  `Cratis.Specifications` keeps the approach of Machine.Specifications (MSpec): `Establish → Because → should_` maps to "Given → When → Then" and keeps each spec focused on *one setup, one action, one set of assertions*.
17
17
 
@@ -97,13 +97,19 @@ The goal is to cover *decisions and transformations* — code where bugs hide. S
97
97
 
98
98
  **Avoid file names starting with:** `when_getting_*`, `when_returning_*` — if a spec name starts with "getting" or "returning", it's probably testing a simple getter, which is not worth specifying.
99
99
 
100
+ ## Choosing the harness
101
+
102
+ Call the code. A function whose result depends only on its arguments is specified by calling it with those arguments and asserting the result: no host, container or harness. Use a harness (scenario, fixture, in-memory engine) for behavior that exists only in the framework: discovery, attribute- or convention-driven wiring, authorization, persistence-time rules and declarative mappings. Name each such spec after the wiring it proves, and do not repeat through the harness a decision a plain call already proves.
103
+
104
+ Substitute collaborators (a service, a clock, a sender) freely in a plain call. Never substitute the event store, a read-model store or the pipeline as proof of persistence, projection or pipeline behavior: either make the code a pure function of its inputs, or keep the harness. Harness coverage is an obligation per behavior the framework owns, not a quota. Fast, parallel-safe harnesses are required by [fast-feedback.md](./fast-feedback.md).
105
+
100
106
  ## Multiple Outcomes
101
107
 
102
108
  Each distinct outcome deserves its own spec file. This keeps specs small, focused, and independently verifiable. When a spec fails, you immediately know *which* outcome broke — no debugging through a multi-assertion file.
103
109
 
104
110
  - When a behavior has multiple outcomes, create a `when_<behavior>/` folder with separate files for each outcome.
105
111
  - For simple behaviors with a single outcome, use a single file: `when_<behavior>`.
106
- - Never write a single file that tests an entire class's unrelated behaviors. **C# declarative read-model mappings are the exception:** use one scenario class per read model, one shared event sequence with captured intermediate states, and many read-only facts for fields and lifecycle outcomes. See [specs.scenarios.csharp.md](./specs.scenarios.csharp.md) for scenario selection and cost budgets.
112
+ - Never write a single file that tests an entire class's unrelated behaviors. **C# declarative read-model mappings are the exception:** use one scenario class per read model by default (another only for a sequence that cannot share it, such as a removal or another key), one shared event sequence with captured intermediate states, and many read-only facts for fields and lifecycle outcomes. See [specs.scenarios.csharp.md](./specs.scenarios.csharp.md) for scenario selection and cost budgets.
107
113
 
108
114
  ## Reusable Context
109
115
 
@@ -14,7 +14,44 @@ Specs are **mandatory for every slice type**, including reactors.
14
14
 
15
15
  ## Start with the plain call; add a scenario where it adds proof
16
16
 
17
- **Judgment-shaped convention:** call pure `Handle()`, validators, reducers and reactor decisions directly with their inputs, including read-model state. Add a scenario only for pipeline/authorization wiring, constraints or declarative projection mapping. Do not repeat every decision through the pipeline; name the extra behavior each scenario proves.
17
+ Call pure `Handle()`, validators, reducers and reactor decisions directly with their inputs, including read-model state. Add a scenario only for the wiring the decision table below obligates (pipeline and authorization, constraints, projection and reducer dispatch, reactor dispatch and routing). Do not repeat every decision through the pipeline; name the extra behavior each scenario proves.
18
+
19
+ **Decision table — apply it per artifact, without judgment.** A plain call proves the decision; a scenario is a coverage obligation for the wiring only the framework proves. These are obligations, not quotas: add a scenario for each behavior listed, and none beyond it.
20
+
21
+ | Artifact | Plain call (`Specification`) | Scenario obligation |
22
+ |---|---|---|
23
+ | Command with a pure `Handle(…)` | every outcome and rejection branch, with read-model state supplied | the pipeline path once (`CommandScenario`: discovery, DI, event source, appended events), plus a **negative proof for each wiring that can refuse before `Handle`**: authorization denies (`ShouldNotBeAuthorized`), validation rejects, `Provide()` short-circuits |
24
+ | `Handle` that needs `IEventStore`, `IReadModels` or services | none: do not substitute the store | keep the scenario; when touched, take the read model as a `Handle` parameter and use the row above |
25
+ | `CommandValidator`, `ConceptValidator`, `Provide()` logic | each rule, built with its state | the command's pipeline scenarios prove the wiring |
26
+ | Constraint (`[Unique]`, `IConstraint`) | none | an `EventScenario` per constraint: the violation and the nearest non-violation |
27
+ | Projection or read model | none | `ReadModelScenario` against the real engine, covering **each event, key, join, children and removal the projection declares**: one class per read model with one shared event sequence by default; add a class only for a sequence that cannot share it (a removal, another key) |
28
+ | Reducer | each transition: `Model.Reduce(event, current[, context])` | a `ReadModelScenario` for dispatch, filters and stream routing |
29
+ | Reactor | each handler decision and side effect, called directly with substitute collaborators | a `ReactorScenario`: dispatch, filtering, read-model parameters, routing of produced events to the right event source, `[OnceOnly]`/replay |
30
+ | Query | extracted filter, sort or composition logic | `QueryScenario` for arguments, authorization and paging; provider behavior is integration |
31
+ | Cross-slice flow | each link | out-of-process integration, outside the fast loop |
32
+
33
+ Name a scenario after the wiring it proves (`and_the_caller_is_not_a_maintainer`). A plain `Handle()` spec never takes a validator, authorization or constraint name: it did not run them. Merge scenario classes only with an assertion map that keeps every assertion. Never use a substitute for the event store or a read-model store as proof of persistence or projection. Cost and parallel-safety requirements are in [fast-feedback.md](./fast-feedback.md).
34
+
35
+ #### Negative authorization proof
36
+
37
+ ```csharp
38
+ #if DEBUG
39
+ namespace MyApp.Authors.Registration.when_registering_an_author;
40
+
41
+ public class and_the_caller_is_not_a_maintainer : Specification
42
+ {
43
+ readonly CommandScenario<RegisterAuthor> _scenario = new(); // no identity registered
44
+ CommandResult _result;
45
+
46
+ async Task Because() => _result = await _scenario.Execute(new RegisterAuthor(AuthorId.New(), "Jane Austen"));
47
+
48
+ [Fact] void should_be_denied() => _result.ShouldNotBeAuthorized();
49
+ [Fact] async Task should_append_nothing() => await _scenario.ShouldNotHaveAppendedEvents();
50
+ }
51
+ #endif
52
+ ```
53
+
54
+ An unauthenticated scenario against an `[Authorize]`/`[Roles]` command is denied before `Handle`; pair it with the happy-path scenario that registers an allowed identity in `_scenario.Services`.
18
55
 
19
56
  **Application specs supply state, not decision-read infrastructure.** Call `Handle()` or the validator directly with supplied read-model state. Never call `UseDecisionReads`, configure protection modes, seed events solely for infrastructure, or re-prove the platform's concurrency, conflict-refusal or isolation matrix; Arc/Chronicle specs own it. The application's **only decision-read spec obligation** is one cheap assembly-level shape-admission spec: `DecisionReadAdmissions.ShouldAdmitDecisionReadsIn(assembly)` (`Cratis.Arc.Chronicle.Testing.Commands`). It checks the application's own models/projections without executing commands or running Chronicle, not the platform guards. Never simulate competing appends either (`AppendConcurrently` or any hand-built interleaving): concurrency is a framework concern a developer should not think about in tests, and Arc and Chronicle specs prove it. A rule that must hold under races is expressed in production by taking the read model as a `Handle` parameter under `[ReadProtection(On)]`; the spec gives that read model's state and asserts the outcome. Report harness state gaps upstream; never weaken production guards for speed. `AppendConcurrently` is an advanced framework-verification API: use only for an explicit need. Direction, no release claim: Arc will obsolete `UseDecisionReads`.
20
57
 
@@ -26,7 +63,7 @@ Specs are **mandatory for every slice type**, including reactors.
26
63
  | `CommandScenario<TCommand>` | the real Arc command pipeline — authorization + validators + `Provide()` + `Handle()` + appended events | pipeline wiring, authorization or constraints adds proof |
27
64
  | `QueryScenario<TReadModel>` | query pipeline: argument conversion, authorization, validation, interceptors; `Observe` buffers emissions | query composition matters; real MongoDB observations still need infrastructure |
28
65
  | `EventScenario` | Chronicle-level append semantics, no command pipeline | constraint violations, raw stream sequencing/concurrency |
29
- | `ReadModelScenario<TReadModel>` | projection/reducer state from a sequence of events (auto-detects model-bound / fluent / reducer) | one class per read model, many read-only assertions on shared setup |
66
+ | `ReadModelScenario<TReadModel>` | projection/reducer state from a sequence of events (auto-detects model-bound / fluent / reducer) | one class per read model by default, many read-only assertions on shared setup (see the decision table for when to add another) |
30
67
  | `ReactorScenario<TReactor>` | reactor handler invocation + side effects via mocked services | the reactor wiring and side effects (Automation / Translation slices) |
31
68
 
32
69
  > **Out-of-process Chronicle integration specs are an advanced case** — reserve them for host wiring, real infrastructure, serialization/transport, or end-to-end boundaries the scenario helpers can't exercise. They are not the default vertical-slice test shape.
@@ -143,7 +180,7 @@ async Task Because() =>
143
180
 
144
181
  ### `ReadModelScenario<TReadModel>` — declarative projections and reducer wiring
145
182
 
146
- The default for declarative projections; pure reducer decisions start with a direct call. Use **one scenario class per read model**, not one per property, mapping attribute or lifecycle event. Apply its event sequence once, capture intermediate states in `Because` where needed, then assert fields, children, counters and lifecycle outcomes in many read-only facts. Add a reducer scenario only for dispatch, filtering, metadata or read-model wiring. Use xUnit `Assert.*` on `_scenario.Instance`.
183
+ The default for declarative projections; pure reducer decisions start with a direct call. Use **one scenario class per read model**, not one per property, mapping attribute or lifecycle event; add another class only for an event sequence that cannot share the first (a removal, another key), as the decision table says. Apply its event sequence once, capture intermediate states in `Because` where needed, then assert fields, children, counters and lifecycle outcomes in many read-only facts. Add a reducer scenario only for dispatch, filtering, metadata or read-model wiring. Use xUnit `Assert.*` on `_scenario.Instance`.
147
184
 
148
185
  ```csharp
149
186
  ReadModelScenario<Author> _scenario = null!;
@@ -20,7 +20,7 @@ This file is the reference for *what* goes in each part of a slice. Layout, slic
20
20
 
21
21
  - .NET / C# (ASP.NET Core) — **Cratis Arc** for CQRS / model-bound commands and queries, **Cratis Chronicle** for event sourcing, MongoDB or EF Core for read models.
22
22
  - React + TypeScript (Vite) — Cratis Components 4.x + Arc-generated proxies, MVVM. Vitest + Mocha/Chai/Sinon for frontend specs.
23
- - xUnit + Cratis.Specifications + NSubstitute for C# specs (`Specification` plus a direct call for pure command/validator/reducer decisions with supplied state; add scenarios only for pipeline/authorization wiring, constraints or declarative projections — see [specs.md](./specs.md)).
23
+ - xUnit + Cratis.Specifications + NSubstitute for C# specs (`Specification` plus a direct call for pure command/validator/reducer decisions with supplied state; add the scenarios the per-artifact decision table in [specs.scenarios.csharp.md](./specs.scenarios.csharp.md) obligates).
24
24
 
25
25
  ## Proxy generation — the build dependency
26
26
 
@@ -11,9 +11,10 @@ metadata:
11
11
  Start a pure decision with a plain method call: `Handle()`,
12
12
  `Handle(providedValue)`, reducer logic or a reactor's decision needs no host,
13
13
  services or harness, including direct validator calls with supplied read-model state.
14
- Add scenarios only for pipeline/authorization wiring, constraints, declarative
15
- mapping; follow the
16
- [scenario selection and cost rule](../../rules/specs.scenarios.csharp.md). The scenario family
14
+ Add exactly the scenarios the per-artifact decision table in the
15
+ [scenario selection and cost rule](../../rules/specs.scenarios.csharp.md) obligates
16
+ (pipeline and authorization, constraints, projection and reducer dispatch,
17
+ reactor dispatch and routing). The scenario family
17
18
  exercises the real Arc and Chronicle code paths in-process — no HTTP, no fixture,
18
19
  no server.
19
20
 
@@ -108,9 +109,12 @@ A behavior covering several subjects groups under
108
109
  ## Step 2 — Start with the decision; add the scenario by slice type
109
110
 
110
111
  Specify a pure decision with the plain `Specification` base first. A direct
111
- `Handle()` call does not run validation, authorization, `Provide()` or operations;
112
- add a scenario only where that wiring adds proof. Keep shared lifecycle enabled
113
- and measure scenario cost as described in the linked rule.
112
+ `Handle()` call does not run validation, authorization, `Provide()` or operations,
113
+ so each wiring that can refuse before `Handle` needs its own negative scenario.
114
+ The per-artifact decision table and its coverage obligations are in the
115
+ [scenario rule](../../rules/specs.scenarios.csharp.md); apply it as written.
116
+ A `Handle` that takes `IEventStore`, `IReadModels` or services is not a plain
117
+ call: keep the scenario, and when touched take read models as parameters.
114
118
 
115
119
  | Slice type | Scenario | Skill |
116
120
  | --- | --- | --- |
@@ -135,11 +139,12 @@ exception diagnostics, synchronous handler throws and UTC context defaults.
135
139
 
136
140
  One specification class for **each** of:
137
141
 
138
- 1. **The happy path** — a plain call returns the expected event; add pipeline
139
- coverage only for behavior that call cannot prove.
142
+ 1. **The happy path** — a plain call returns the expected event, plus one
143
+ `CommandScenario` happy path for the pipeline, and a negative scenario for
144
+ each authorization policy and `Provide()` short-circuit.
140
145
  2. **Each validation failure** — instantiate `CommandValidator` or
141
- `ConceptValidator` with the required state and validate directly. Use a
142
- scenario only to prove pipeline wiring.
146
+ `ConceptValidator` with the required state and validate directly; the
147
+ command's pipeline scenario proves the wiring.
143
148
  3. **Each business-rule rejection** — one `and_` class per condition in
144
149
  `Handle()` that inspects a read model.
145
150
  4. **Each constraint violation** — one class per constraint, written with
@@ -48,7 +48,7 @@ ingress above, otherwise configure a credential-validating scheme. The old
48
48
  same host-wide flag. See [authentication](references/authentication.md).
49
49
  Source: [secure defaults](https://github.com/Cratis/Arc/blob/main/Documentation/upgrading/secure-defaults.mdx).
50
50
 
51
- Cratis AuthProxy is the ingress that meets these obligations for Cratis applications. Read [AuthProxy](references/authproxy.md) when the application is deployed behind it (header contract, `/.cratis/me` fail-closed verification, tenant headers).
51
+ Cratis AuthProxy is the ingress that meets these obligations for Cratis applications. Read [AuthProxy](references/authproxy.md) when the application is deployed behind it (header contract, `/.cratis/me` fail-closed verification, tenant headers, and starting it from a Screenplay model's provider list and a Cratis environment).
52
52
 
53
53
  ## Identity details — what the frontend is told
54
54