@cratis/pi 2.22.0 → 2.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/package/corpus/agents/backend-developer.md +6 -5
  2. package/package/corpus/agents/code-reviewer.md +7 -7
  3. package/package/corpus/agents/coordinator.md +5 -4
  4. package/package/corpus/agents/frontend-developer.md +34 -40
  5. package/package/corpus/agents/orchestrator.md +5 -4
  6. package/package/corpus/agents/performance-reviewer.md +5 -4
  7. package/package/corpus/agents/planner.md +5 -4
  8. package/package/corpus/agents/repository-investigation-reviewer.md +2 -1
  9. package/package/corpus/agents/repository-investigator.md +2 -1
  10. package/package/corpus/agents/security-reviewer.md +5 -4
  11. package/package/corpus/agents/slice-implementer.md +8 -2
  12. package/package/corpus/agents/spec-writer.md +6 -4
  13. package/package/corpus/harnesses/pi/extensions/cratis-rules/index.ts +233 -10
  14. package/package/corpus/harnesses/pi/extensions/subagent/agents.ts +52 -7
  15. package/package/corpus/harnesses/pi/extensions/subagent/index.ts +14 -0
  16. package/package/corpus/hooks/README.md +29 -27
  17. package/package/corpus/hooks/agent-stop.md +1 -5
  18. package/package/corpus/hooks/scripts/cratis-patterns.json +1 -1
  19. package/package/corpus/hooks/scripts/validate-package-subpaths.sh +1 -2
  20. package/package/corpus/prompts/ship-changes.prompt.md +4 -0
  21. package/package/corpus/prompts/verify-ai-setup.prompt.md +17 -7
  22. package/package/corpus/rules/ai-distribution.md +60 -0
  23. package/package/corpus/rules/application-profile.md +73 -0
  24. package/package/corpus/rules/components.md +77 -44
  25. package/package/corpus/rules/dialogs.md +24 -20
  26. package/package/corpus/rules/efcore.md +35 -187
  27. package/package/corpus/rules/framework.md +1 -1
  28. package/package/corpus/rules/frontend-quality.md +1 -1
  29. package/package/corpus/rules/frontend-testing.md +4 -4
  30. package/package/corpus/rules/general.md +21 -162
  31. package/package/corpus/rules/guards-and-fuses.md +24 -10
  32. package/package/corpus/rules/local-work-artifacts.md +6 -0
  33. package/package/corpus/rules/new-repository-intake.md +34 -0
  34. package/package/corpus/rules/orleans.md +13 -27
  35. package/package/corpus/rules/profiles.md +6 -0
  36. package/package/corpus/rules/react.md +12 -12
  37. package/package/corpus/rules/reactors.md +28 -5
  38. package/package/corpus/rules/rtk.md +13 -3
  39. package/package/corpus/rules/specs.scenarios.csharp.md +18 -3
  40. package/package/corpus/rules/specs.typescript.md +1 -1
  41. package/package/corpus/rules/storybook.md +2 -2
  42. package/package/corpus/rules/typescript.md +1 -0
  43. package/package/corpus/rules/verification-discipline.md +7 -1
  44. package/package/corpus/rules/vertical-slices.md +20 -15
  45. package/package/corpus/rules/writing-correct-examples.md +1 -0
  46. package/package/corpus/skills/cratis-application-react-specifications/SKILL.md +4 -4
  47. package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +16 -8
  48. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +6 -4
  49. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +1 -1
  50. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +1 -1
  51. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +1 -1
  52. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +1 -1
  53. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +7 -4
  54. package/package/corpus/skills/cratis-arc-command/SKILL.md +9 -7
  55. package/package/corpus/skills/cratis-arc-command/references/command-result.md +1 -1
  56. package/package/corpus/skills/cratis-arc-command/references/handler-shapes.md +2 -2
  57. package/package/corpus/skills/cratis-arc-command/references/proxy-generation.md +6 -3
  58. package/package/corpus/skills/cratis-arc-command/references/read-model-injection.md +1 -1
  59. package/package/corpus/skills/cratis-arc-command-execution/SKILL.md +12 -2
  60. package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +5 -3
  61. package/package/corpus/skills/cratis-arc-ef-core-migration/SKILL.md +7 -4
  62. package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +10 -3
  63. package/package/corpus/skills/cratis-arc-query-paging/SKILL.md +13 -8
  64. package/package/corpus/skills/cratis-arc-react-page/SKILL.md +27 -24
  65. package/package/corpus/skills/cratis-arc-react-page/references/data-page.md +15 -11
  66. package/package/corpus/skills/cratis-arc-react-page/references/data-tables.md +9 -2
  67. package/package/corpus/skills/cratis-arc-react-page/references/dialogs.md +11 -9
  68. package/package/corpus/skills/cratis-arc-react-page/references/queries-and-commands.md +26 -2
  69. package/package/corpus/skills/cratis-chronicle-cli-operations/SKILL.md +19 -13
  70. package/package/corpus/skills/cratis-chronicle-client-dotnet/SKILL.md +17 -15
  71. package/package/corpus/skills/cratis-chronicle-client-elixir/SKILL.md +24 -20
  72. package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +26 -15
  73. package/package/corpus/skills/cratis-chronicle-client-typescript/SKILL.md +15 -11
  74. package/package/corpus/skills/cratis-chronicle-compliance/SKILL.md +14 -3
  75. package/package/corpus/skills/cratis-chronicle-event-constraints/SKILL.md +13 -6
  76. package/package/corpus/skills/cratis-chronicle-event-modeling/SKILL.md +2 -2
  77. package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +11 -5
  78. package/package/corpus/skills/cratis-chronicle-event-type-migration/SKILL.md +2 -2
  79. package/package/corpus/skills/cratis-chronicle-multi-tenancy/SKILL.md +2 -2
  80. package/package/corpus/skills/cratis-chronicle-projection/SKILL.md +23 -4
  81. package/package/corpus/skills/cratis-chronicle-projection/references/fluent-builder.md +1 -1
  82. package/package/corpus/skills/cratis-chronicle-projection/references/model-bound-attributes.md +2 -2
  83. package/package/corpus/skills/cratis-chronicle-reactor/SKILL.md +22 -8
  84. package/package/corpus/skills/cratis-chronicle-read-model/SKILL.md +1 -1
  85. package/package/corpus/skills/cratis-chronicle-read-model/references/queries.md +1 -1
  86. package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +3 -1
  87. package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +1 -1
  88. package/package/corpus/skills/cratis-chronicle-web-workbench/SKILL.md +19 -11
  89. package/package/corpus/skills/cratis-cli-terminal-workbench/SKILL.md +11 -6
  90. package/package/corpus/skills/cratis-code-review/SKILL.md +8 -6
  91. package/package/corpus/skills/cratis-components-accessibility/SKILL.md +112 -82
  92. package/package/corpus/skills/cratis-components-schema-editor/SKILL.md +12 -10
  93. package/package/corpus/skills/cratis-components-stepper-command-dialog/SKILL.md +11 -12
  94. package/package/corpus/skills/cratis-components-styling/SKILL.md +204 -170
  95. package/package/corpus/skills/cratis-components-toolbar/SKILL.md +32 -26
  96. package/package/corpus/skills/cratis-event-model-diagram/SKILL.md +1 -1
  97. package/package/corpus/skills/cratis-fundamentals-concept/SKILL.md +2 -2
  98. package/package/corpus/skills/cratis-fundamentals-type-discovery/SKILL.md +2 -2
  99. package/package/corpus/skills/cratis-lens-browser-extension/SKILL.md +4 -2
  100. package/package/corpus/skills/cratis-performance-review/SKILL.md +4 -2
  101. package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +15 -5
  102. package/package/corpus/skills/cratis-security-review/SKILL.md +20 -3
  103. package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +3 -3
  104. package/package/corpus/skills/cratis-specifications-csharp/references/application-scenarios.md +12 -8
  105. package/package/corpus/skills/cratis-specifications-csharp/references/csharp-patterns.md +1 -1
  106. package/package/corpus/skills/cratis-specifications-csharp/references/integration-specs.md +7 -3
  107. package/package/corpus/skills/cratis-specifications-typescript/SKILL.md +6 -3
  108. package/package/corpus/skills/cratis-specifications-typescript/references/typescript-patterns.md +21 -4
  109. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +29 -17
  110. package/package.json +1 -1
  111. package/package/corpus/rules/terminal-commands.md +0 -19
@@ -69,7 +69,7 @@ Behavior that can regress without TypeScript catching it:
69
69
  - Command orchestration outside `CommandDialog`: handling of unauthorized / invalid / exception / success outcomes.
70
70
  - React component rendering only when markup, wrapper behavior, disabled state, or integration with a wrapped component is the point.
71
71
 
72
- **Do not test:** generated Cratis proxies; framework/PrimeReact internals; CSS pixel-perfection; trivial presentational pass-throughs with no branch/derived behavior.
72
+ **Do not test:** generated Cratis proxies; framework/Cratis Components internals; CSS pixel-perfection; trivial presentational pass-throughs with no branch/derived behavior.
73
73
 
74
74
  ## View models
75
75
 
@@ -90,9 +90,9 @@ import React from 'react';
90
90
  import { renderToStaticMarkup } from 'react-dom/server';
91
91
  import { vi } from 'vitest';
92
92
 
93
- vi.mock('primereact/dialog', () => ({
94
- Dialog: (props: { footer?: React.ReactNode; children?: React.ReactNode }) =>
95
- React.createElement('div', null, props.footer, props.children),
93
+ vi.mock('@cratis/components/Dialogs', () => ({
94
+ Dialog: (props: { buttons?: React.ReactNode; children?: React.ReactNode }) =>
95
+ React.createElement('div', null, props.children, props.buttons),
96
96
  }));
97
97
 
98
98
  describe('when rendered while busy', () => {
@@ -2,7 +2,7 @@
2
2
 
3
3
  Cratis repositories come in **two profiles**, and the rules are scoped to them. **Identify your profile first** — it decides which rules apply.
4
4
 
5
- - **Application profile (default)** — you are *building an application on Cratis*: event-sourced CQRS with **Cratis Chronicle** + **Cratis Arc**, vertical slices, read models persisted to MongoDB/EF Core, and a React + Cratis Components (PrimeReact) frontend in MVVM. Most of this corpus targets this profile.
5
+ - **Application profile (default)** — you are *building an application on Cratis*: event-sourced CQRS with **Cratis Chronicle** + **Cratis Arc**, vertical slices, read models persisted to MongoDB/EF Core, and a React + Cratis Components (4.x) frontend in MVVM. Most of this corpus targets this profile.
6
6
  - **Framework profile** — you are *contributing to a Cratis framework repository itself* (Arc, Chronicle, Fundamentals, Components, …). These are **libraries** — source generators, the Chronicle kernel (Orleans grains + storage), client SDKs, a React component library — **not** vertical-slice event-sourced apps. The application-architecture rules here **do not apply**; follow **[framework.md](./framework.md)**.
7
7
 
8
8
  **How to tell:** if the repo's own package is `Cratis.*` / `@cratis/*` and it *builds* the framework, you are in the framework profile. If it *consumes* Cratis to build a product, you are in the application profile.
@@ -76,66 +76,17 @@ immediately before acting and stop only when drift invalidates the authorized sc
76
76
  or recovery plan. Git history rewrites remain prohibited unless the user explicitly
77
77
  requests one.
78
78
 
79
- ## New Repository Strategy Intake
80
-
81
- When a repository is created in the Cratis organization, prepare one transient,
82
- no-effect Strategy intake proposal. Do not create, comment on, assign, mention,
83
- link, or otherwise mutate a GitHub issue unless a current repository profile and
84
- the exact operation are separately accepted. Tool access and a request to create
85
- the repository do not supply that issue-effect authority.
86
-
87
- The proposal should include only the bounded facts needed for Strategy triage:
88
-
89
- - repository name, URL, visibility, and creation state;
90
- - purpose, intended users, lifecycle, and whether it is canonical, generated,
91
- experimental, operational, or scheduled for retirement;
92
- - accountable owner or explicit vacancy and cross-repository boundaries;
93
- - upstream/downstream dependencies and current owning records;
94
- - release, distribution, credential, security, privacy, compliance, and data
95
- expectations; and
96
- - requested Strategy identity, portfolio, metadata, ownership, and local AI
97
- context review.
98
-
99
- First decide whether no record, an existing Strategy record, a bounded update
100
- proposal, a new proposal, or a sensitive human route is appropriate. Treat the
101
- result as Strategy intake, not strategic approval. Do not invent Strategy
102
- metadata or copy private Strategy content into a public repository. Repository
103
- creation does not require an issue URL; unresolved Strategy reconciliation is an
104
- explicit next action for the authorized human/owning process.
105
-
106
79
  ## Shared AI Distribution
107
80
 
108
- Do not copy or synchronize shared `.cratis/ai`, `.agents`, `.claude`, `.github`, or
109
- `.pi` trees from one Cratis repository to another. A consuming repository must
110
- never become an accidental source that republishes its local AI corpus.
111
-
112
- Shared Cratis capabilities are authored and approved in `Cratis/AI`, generated
113
- into `Cratis/AI.Distribution`, and installed only from an immutable reviewed
114
- version after its release gates pass. Keep existing repository-local AI files in
115
- place while the replacement distribution remains under canary; do not restart
116
- legacy all-to-all propagation and do not delete legacy adapters before reviewed
117
- retirement evidence exists.
118
-
119
- Shared public product and `engineering-*` packages contain only public-safe
120
- Cratis behavior. The `engineering-` prefix identifies the maintainer audience;
121
- it does not imply confidential package contents or a private registry.
122
-
123
- The consuming repository owns its project facts, confidential behavior, local
124
- skills, and minimal host bootstraps. Use repository-owned documentation as canonical
125
- project context when that migration is active, `.agents/skills/` for private or
126
- repository-specific local workflows, and repository-owned documentation only as the
127
- documented legacy context fallback. Never merge, overwrite, or remove these
128
- local files as a side effect of installing, updating, rolling back, or
129
- uninstalling shared AI capabilities.
130
-
131
- Keep confidential and repository-specific behavior local. Generalize and remove
132
- private facts before proposing a reusable improvement to `Cratis/AI`; never
133
- reverse-sync a private repository's AI tree or generated adapters.
134
-
135
- Update shared AI by changing a version pin through the approved organization or
136
- host mechanism. Canary the new version, observe its behavior and gates, and roll
137
- back by version when needed. Never patch generated distribution bytes or
138
- marketplace wrappers by hand.
81
+ Two invariants; the mechanics of every channel (`cratis ai install`, `@cratis/pi`,
82
+ the plugin marketplaces) are in [ai-distribution.md](./ai-distribution.md):
83
+
84
+ - Never copy or synchronize `.cratis/ai`, `.agents`, `.claude`, `.github`, or `.pi`
85
+ trees between repositories, and never patch managed files under `.cratis/ai/`
86
+ by hand — update through the channel that installed them.
87
+ - The consuming repository owns its project facts, confidential behavior, and
88
+ local skills; installing, updating, or uninstalling shared AI never merges,
89
+ overwrites, or removes them.
139
90
 
140
91
  ## Verification Discipline
141
92
 
@@ -144,98 +95,13 @@ A claim is only as good as the signal behind it — a build result, a test run,
144
95
  - **Confirm "done"/"fixed"/"correct" against a fresh signal — never self-assessment.** Run the relevant gate and observe it pass *this time*.
145
96
  - **After a fix, re-run the gate that failed.** Don't argue yourself to green.
146
97
  - **A green build is not behavioral correctness.** Compilation proves it builds, not that the slice does the right thing — that's what specs and exercising the UI are for.
147
- - **Report with inspectable evidence, and name what you didn't verify.**
98
+ - **Report the conclusion and what you didn't verify, in a line or two.** Show the output when asked, when a claim is contested, or when the check failed — see [verification-discipline.md](./verification-discipline.md).
148
99
 
149
100
  ---
150
101
 
151
102
  # Application profile
152
103
 
153
- > The following — **Project Layout, Slice Types, Slice Naming, the Rules, and the Implementation Workflow** — applies when **building an application on Cratis**. If you are contributing to a Cratis framework repo, skip to **Framework profile** below and follow [framework.md](./framework.md).
154
-
155
- ## Project Layout (Cratis Application convention)
156
-
157
- The framework discovers commands and read models by attributes and static methods — **the folder shape is a convention, not a requirement.** The house default keeps everything for one behavior together, with **no top-level `Features/` wrapper**: the domain hierarchy lives directly under the app source root.
158
-
159
- ```
160
- <AppSourceRoot>/ e.g. Source, Source/Core (app-defined)
161
- ├── Common/ shared ConceptAs<T> / EventSourceId<T> types
162
- ├── Identity/ Components/ ... cross-cutting / shared concerns, at the top level
163
- └── <Module>/ top-level domain area — natural for most apps, NOT required
164
- └── <Feature>/ grouping within the domain area
165
- ├── <Feature>.tsx pass-through layout (renders <Outlet/>)
166
- ├── <Concept>.cs feature-level concept types
167
- └── <Slice>/ one folder per behavior — the invariant unit
168
- ├── <Slice>.cs backend artifacts for the slice in one file
169
- ├── *.tsx React component(s) for the slice
170
- └── when_*/ spec folders
171
- ```
172
-
173
- **The slice is the invariant unit** — one behavior (command + events + projection + component + specs), created/renamed/deleted together. Features group related slices. A `<Module>` is the natural top-level domain grouping for larger areas (e.g. `Accounts`, `Admin`, `Requests`) but is **not required** — a feature may sit directly under the source root when no module grouping is natural; depth follows what fits the application. Cross-cutting concerns (shared concepts in `Common/`, shared components, identity) live at the top level. Namespace mirrors the path under `<AppSourceRoot>` (`<RootNamespace>.<Module>.<Feature>.<Slice>`, dropping any level that isn't present). Splitting the backend file is allowed when a slice grows large or shared concepts move upward; the single-file shape is the default, not a mandate.
174
-
175
- > **No top-level `Features/` wrapper** — modules/features live directly under the app source root. The framework enforces no layout; this nested domain hierarchy is the chosen Cratis Application convention.
176
-
177
- ## Slice Types
178
-
179
- Pick exactly one type per slice folder — determined by what the slice *does*.
180
-
181
- | Type | What it does | Contents |
182
- | --- | --- | --- |
183
- | **State Change** | Accepts a command, appends events | Command + validator + event(s); optional `[Passive]` read model for command-side decisions |
184
- | **State View** | Projects events into a queryable read model | `[ReadModel]` + model-bound projection + static query method(s) |
185
- | **Automation** | Reacts to events, calls external systems / `ICommandPipeline` | Reactor only |
186
- | **Translation** | Reacts to events and appends follow-up events to another stream | Reactor only |
187
-
188
- ## Slice Naming (convention)
189
-
190
- Commands are imperative intents (`Register`, `Create`); the slice folder is the action only, never repeating a noun the Feature already establishes. **`[EventType]` records are past-tense facts** and must be self-describing (`AuthorRegistered`, never `Created`) — this past-tense, one-purpose naming is a Chronicle framework recommendation. Static query methods are descriptive reads (`AllAuthors`, `AuthorById`, `AuthorsByName`).
191
-
192
- ## Rules
193
-
194
- Tagged **[contract]** (framework-enforced) or **[convention]** (house default). Mechanics and examples live in `vertical-slices.md`.
195
-
196
- 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. Drop to fluent (`IProjectionFor<T>`, `IConstraint`) only when model-bound can't express the rule.
197
- 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**, inject the read model into `Handle()` and return a typed error via `Result<TEvent, ValidationResult>`. **Throwing from `Provide()`/`Handle()` is an exception (HTTP 500), not a validation rejection** — throw only for genuinely exceptional conditions, never for normal business rejection.
198
- 3. **[contract] Event-source id resolution order:** `ICanProvideEventSourceId` → an `EventSourceId`/`EventSourceId<T>`-derived value → `[Key]` → else Arc/Chronicle 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`.
199
- 4. **[contract] Events never carry the event-source id** — it is implicit in the event context.
200
- 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.
201
- 6. **[contract] Avoid nullable event properties** — Chronicle's analyzer warns on them. Model optional facts as a separate event; resolve nullable command inputs to a non-null sentinel before constructing the event.
202
- 7. **[contract] `[EventType]` takes no arguments for new events** — the type name is the identifier. Use `generation:`/id only when evolving an existing contract; schema changes get a new generation + an `EventTypeMigration<TUpgrade,TPrevious>` (never edit stored-event semantics silently). An enum that only gains a member, or has one renamed, is the exception — Chronicle accepts that in place; a *removed* or *renumbered* member still needs a generation, plus a value map saying what the old values became.
203
- 8. **[convention] Every `[EventType]` has an XML `<summary>`** — a Cratis C# documentation convention (not a Chronicle rule); events live in the log forever, so record why they exist.
204
- 9. **[convention] `[ReadModel]` properties carry no default values** except semantically meaningful enum initial states and `[SetValue<T>]`-driven `bool` flags. False defaults hide missing projection wiring.
205
- 10. **[contract] AutoMap is on by default — never call `.AutoMap()`.** Match property names so AutoMap wires them; diverge with `[SetFrom<T>]` / fluent `.Set().To()` only for genuine name differences. Re-enable `.AutoMap()` only inside a scope where it was disabled with `.NoAutoMap()`.
206
- 11. **[contract] Projections consume events and event context — never other read models.** Default to model-bound attributes; use fluent `IProjectionFor<T>` for joins/nested/context/transforms; use a reducer when the model is "current state + event → next state" (a valid style, not a failure mode).
207
- 12. **[contract] Model-bound query custom paths use `[Path("...")]`** (`PathAttribute`), not ASP.NET `[Route]`. Reserve `[Route]` for controller-based endpoints (which this convention avoids).
208
- 13. **[convention] Cross-slice access is read-only through Chronicle** — inject another slice's read model or reference its events; never instantiate or DI another slice's command/handler/service.
209
- 14. **[contract] Never inject `IEventLog` into `Handle()`** — express appends through return types (`IEnumerable<object>` with `EventForEventSourceId` wrappers for cross-stream). In application reactors, return side-effect events or use `ICommandPipeline`; don't reach for `IEventLog` directly.
210
- 15. **[contract] Never edit a generated file** — proxies carry a `// @generated by Cratis` header. Fix the C# source and rebuild.
211
- 16. **[convention] Use the Cratis dialog wrappers** — never import `Dialog` from `primereact/dialog`; use `CommandDialog` / `Dialog` from `@cratis/components`. The default frontend stack is Cratis Components on PrimeReact theming/tokens/`pt` — **not** Tailwind (Tailwind is one supported unstyled path, not the generic default).
212
- 17. **[convention] One slice is one unit** — creating/renaming/moving/deleting a slice means doing the same to every artifact (the `.cs`, every `when_*/`, every `.tsx`, the composition import/JSX, the route).
213
-
214
- ## Implementation Workflow
215
-
216
- - **Phase 0 — Model the request.** Confirm Module/Feature, Slice name, slice type, domain rules. For new behavior or unclear event vocabulary, run the **event-modeling** skill before writing code.
217
- - **Phase 1 — Backend.** Implement a coherent slice change. **Gate:** incrementally build the affected Debug project to regenerate proxies and compile spec code; add Release verification when required for cross-cutting or merge/release gates (see the proxy-generation note below).
218
- - **Phase 2 — Specs.** Mandatory for every slice type, in-process scenario family first: `CommandScenario<T>` (commands), `EventScenario` (constraints/append), `ReadModelScenario<T>` (projections/reducers), `ReactorScenario<T>` (reactors). Reserve out-of-process integration specs for host/infra/transport boundaries. **Gate:** tests pass.
219
- - **Phase 3 — Frontend.** Proxies now exist. Build React components from generated proxies, register in the composition page, wire routing. **Gate:** lint, conditional test, and build all clean.
220
-
221
- **Backend before frontend, always** — the frontend depends on proxies that only exist after a successful Debug build. After a coherent set of changes, incrementally build/compile the affected project and run targeted regression checks before proceeding; do not build after every file.
222
-
223
- **Proxy generation runs on Debug, not Release.** `dotnet build -c Debug` is the canonical trigger for regenerating TypeScript proxies — it carries the fullest, most reliably-emitted PDB debug information the proxy generator relies on to place generated files. Generate proxies with a Debug build first; when you (or an agent) subsequently build Release purely to verify the app compiles in that configuration, skip proxy regeneration so the second build can't re-run the generator against a different compilation and touch already-correct generated files: `dotnet build -c Release -p:CratisProxiesOutputPath=`. The empty override clears the output path property the generator's MSBuild target is conditioned on, so the target no-ops for that invocation — no generated file is read or written.
224
-
225
- ## Quality Gates
226
-
227
- | Phase | Command (app-pinned) | Pass criteria |
228
- | --- | --- | --- |
229
- | Backend | build (Debug) | zero errors, zero warnings — validates `#if DEBUG` spec code and regenerates proxies |
230
- | Backend | build (Release) | zero errors, zero warnings — build-only check; pass `-p:CratisProxiesOutputPath=` to skip re-running proxy generation |
231
- | Specs | test | zero failures |
232
- | Frontend | lint | zero errors |
233
- | Frontend | test | zero failures when frontend specs/behavior changed |
234
- | Frontend | build | zero errors |
235
-
236
- Run affected-project incremental checks after a coherent change, then targeted regression tests for the changed behavior. Re-run a failed gate after a relevant fix. Reserve wider matrices and clean/Release builds for cross-cutting changes, demonstrated stale outputs, or required merge/release gates. Documentation/rule-only edits need relevant Markdown, frontmatter, link, and corpus checks, not an application build. Diagnose unrelated or environmental failures within a bounded attempt; report the evidence and blocker instead of broadening scope or retrying indefinitely. Required gates remain blocking until satisfied; never silently waive red CI.
237
-
238
- Documentation-only changes use repository-supported non-release intent, ordinarily `no-release`; confirm the workflow contract rather than assuming a label or API state. Run relevant content, link, frontmatter, and corpus checks instead of unrelated application builds, and satisfy every repository-required check, including release-intent checks where supported. Documentation is never a blanket exemption from red CI. See [pull-requests.md](./pull-requests.md).
104
+ > **Building an application on Cratis?** Project layout, slice types, slice naming, the seventeen slice rules, the implementation workflow and the application quality gates are in [application-profile.md](./application-profile.md), which loads only for repositories that select an application profile. If you are contributing to a Cratis framework repo, see **Framework profile** below.
239
105
 
240
106
  ---
241
107
 
@@ -258,7 +124,7 @@ Documentation-only changes use repository-supported non-release intent, ordinari
258
124
  | For | Location |
259
125
  | --- | --- |
260
126
  | **AI Corpus Profiles** — what profiles exist and how to configure them | `profiles.md` |
261
- | **Contributing to a Cratis framework repo** (framework profile) | `framework.md` |
127
+ | **Contributing to a Cratis framework repo** (framework profile) | `framework.md`; creating a repository in the Cratis organization: `new-repository-intake.md` |
262
128
  | Slice anatomy (commands, `Provide()`, validators, events, projections, read models, reactors, constraints, compliance, cross-slice) | `vertical-slices.md` |
263
129
  | C# / TypeScript style | `csharp.md`, `typescript.md` |
264
130
  | Service lifetimes — why anything taking a scoped dependency is scoped or transient, never a singleton | `csharp.md` |
@@ -268,18 +134,18 @@ Documentation-only changes use repository-supported non-release intent, ordinari
268
134
  | Spec patterns — the four `*Scenario` helpers (application only) | `specs.scenarios.csharp.md` |
269
135
  | Strongly-typed values (`ConceptAs<T>`, `EventSourceId<T>`) | `concepts.md` |
270
136
  | Shared term definitions (event, projection, reducer, reactor, observer, DCB, …) | `glossary.md` |
271
- | Diagnosing a misbehaving slice (read model stale, proxy missing, quarantine, …) | the **diagnose-slice** skill |
272
- | Inspecting or operating a **running** Chronicle store (failed partitions, replays, browsing events) with the `cratis` CLI | the **inspect-running-chronicle** skill |
137
+ | Diagnosing a misbehaving slice | read model stale or startup crash: **cratis-chronicle-projection** (*Startup-crash traps*); reactor paused or quarantined: **cratis-chronicle-reactor** (*Failure behavior*); proxy missing: **cratis-arc-command** (*Generate the TypeScript proxy*) |
138
+ | Inspecting or operating a **running** Chronicle store (failed partitions, replays, browsing events) with the `cratis` CLI | the **cratis-chronicle-cli-operations** and **cratis-cli-terminal-workbench** skills |
273
139
  | EF Core read models / migrations | `efcore.md`, `efcore.specs.md` |
274
140
  | PRs / commits | `pull-requests.md`, `git-commits.md` |
275
- | Reading, citing and superseding a decision record | `decision-records.md` |
141
+ | Reading, citing and superseding a decision record | the **cratis-engineering-decision-record** skill |
276
142
  | Whether you are allowed to do the thing you are able to do | `capability-is-not-authority.md` |
277
- | What must stop and ask a human | `human-verdicts.md` |
143
+ | What must stop and ask a human | `capability-is-not-authority.md` (absent authority for a consequential effect: stop and ask) |
278
144
  | What counts as evidence that something works | `verification-discipline.md` |
279
- | `next:` / `blocker:` values and when a comment is warranted | `work-records-and-comments.md` |
145
+ | Where session notes, plans and handovers may live | `local-work-artifacts.md` |
280
146
  | Exit-code meaning and wrappers that lose a verdict | `exit-codes-and-wrappers.md` |
281
147
  | Writing a scan, allowlist or destructive pass that cannot pass vacuously | `guards-and-fuses.md` |
282
- | The section skeleton every engineering recipe follows | `engineering-recipe-skeleton.md` |
148
+ | How the shared corpus is installed, updated and rolled back | `ai-distribution.md` |
283
149
  | Event modeling / schema migration / calling commands from code / paging / cross-cutting metadata / multi-tenancy | the matching skills |
284
150
  | Step-by-step recipes | `.cratis/ai/skills/` |
285
151
 
@@ -289,7 +155,7 @@ Documentation-only changes use repository-supported non-release intent, ordinari
289
155
  - **Skills and rules are the authoritative answer.** If not answered there, ask. Don't infer Cratis behavior from package internals.
290
156
  - Only make high-confidence suggestions.
291
157
  - Don't change dependency manifests / lockfiles / `global.json` / NuGet config unless explicitly asked.
292
- - When asked to commit, push, create a PR, ship, or land changes, use the **ship-changes** skill.
158
+ - When asked to **ship** or **land** changes, use the **ship-changes** prompt (`.cratis/ai/prompts/ship-changes.prompt.md`); invoking it is what authorizes the branch → commits → push → PR → merge → cleanup chain it describes. A request to only commit, only push, or only open a PR authorizes exactly that step, under [Git commits](./git-commits.md) and [Pull requests](./pull-requests.md) — do not route it through ship-changes and do not add the later steps.
293
159
 
294
160
  ## General
295
161
 
@@ -305,11 +171,4 @@ Documentation-only changes use repository-supported non-release intent, ordinari
305
171
 
306
172
  ## Local AI work artifacts — `.ai-work/` only
307
173
 
308
- AI-assisted sessions produce working artifacts: plans, handover documents, session notes, continuation prompts, status boards, scratch analyses, research dumps. These are **work records, not documentation**:
309
-
310
- - Create every such artifact inside **`.ai-work/`** at the repository root — never at the repository root itself, never under documentation folders, never anywhere else.
311
- - `.ai-work/` is gitignored and must stay untracked. Never commit anything inside it, never `git add -f` anything inside it, and never remove the ignore entry.
312
- - These artifacts must never enter git history or reach GitHub — not on any branch. If you find an unrelated tracked work record, report its path and obtain explicit authorization before moving it into `.ai-work/`, removing it from tracking, or making a dedicated cleanup commit. Discovery alone does not authorize unrelated changes or a commit.
313
- - A genuine follow-up that must survive the session is **not** a work record — suggest opening a GitHub issue for it (or open one when asked) so future work is tracked where everyone can see it, instead of leaving a planning file behind.
314
- - Knowledge that must outlive the session belongs in the repository's documentation structure through normal review, not in a work record.
315
- - **A decision log is not a work record.** A decision — a durable choice with a decider and a date — is documentation: it lives in **`decisions/`** (or the repository's documented decisions folder) and is reviewed like any other documentation. A handover may summarize decisions; it never holds the only copy.
174
+ Plans, handovers, session notes, scratch analyses and research dumps are **work records, not documentation**: they live only in the gitignored `.ai-work/` at the repository root, never enter git history, and are never the only copy of a durable decision (those go in `decisions/`). A follow-up that must outlive the session is an issue, not a file. Full rule: [local-work-artifacts.md](./local-work-artifacts.md).
@@ -8,6 +8,11 @@ A guard that cannot fail is worse than no guard: it converts "nobody looked" int
8
8
  check. Every line is tagged **[contract]** (binding) or **[convention]** (the house
9
9
  default) per the Three Levels of Authority in [`general.md`](./general.md).
10
10
 
11
+ **Scope.** This rule governs two situations: *authoring* a scanner, guard, allowlist or
12
+ checker, and *running a pass* that mutates a computed set of subjects rather than the
13
+ targets the request named. An ordinary edit, review, fix, or a change to the one file the
14
+ user pointed at is outside it and carries none of the machinery below.
15
+
11
16
  ## Non-vacuity
12
17
 
13
18
  - **[contract] A scan over a possibly-empty population carries a non-vacuity check.**
@@ -28,19 +33,28 @@ default) per the Three Levels of Authority in [`general.md`](./general.md).
28
33
  - **[convention] Prefer an expiry to a permanent exemption.** An entry nobody revisits is
29
34
  a rule quietly deleted.
30
35
 
31
- ## Destructive passes
36
+ ## Bulk and irreversible passes
37
+
38
+ These contracts are keyed to the *risk class* of the effect — bulk deletion, history
39
+ rewriting, cross-repository migration, anything irreversible or high-fanout — not to
40
+ whether a person is watching. An autonomous session runs them exactly as an interactive
41
+ one does.
32
42
 
33
43
  - **[contract] Distinguish "subject set empty" from "qualifying set empty".** Finding no
34
44
  candidates at all is a different situation from finding candidates that none qualified;
35
- an unattended pass must refuse to proceed on the first.
36
- - **[contract] Every unattended destructive pass carries a per-pass fuse** — a maximum
37
- number of subjects it may act on in one run, which stops the run rather than trimming
38
- the work silently.
39
- - **[contract] Prepare the inverse before the forward action.** If an exact inverse or a
40
- safe compensation cannot be prepared, stop.
45
+ a pass must refuse to proceed on the first.
46
+ - **[contract] Every pass carries a per-pass fuse** — a maximum number of subjects it may
47
+ act on in one run, which stops the run rather than trimming the work silently.
48
+ - **[contract] Know the recovery before the forward action.** For a reversible effect,
49
+ know how it is undone. For an irreversible one, preserve what the recovery needs first —
50
+ a backup ref, a copy under `.ai-work/keep/`, the list of targets — and stop if nothing
51
+ can be preserved and the request did not name the targets. Do not demand an exact
52
+ inverse where none can exist.
41
53
  - **[contract] Re-read preconditions immediately before each mutation and stop when drift
42
54
  invalidates the authorized scope, safety assumptions, or recovery plan.** Benign drift
43
55
  within an already authorized bounded pass does not require another confirmation.
44
- - **[convention] Dry-run output is the review artifact for a destructive pass whose exact
45
- targets were not already established in the conversation.** A user who has reviewed
46
- and authorized those targets is not asked to approve the same pass again.
56
+ - **[convention] When the targets were not already established in the conversation, show
57
+ the dry-run list and act on it once the user has answered.** A user who has reviewed
58
+ and authorized those targets is not asked to approve the same pass again. The list is a
59
+ message in the conversation, not a retained artifact: do not write receipts, ledgers,
60
+ snapshots or escrow copies of it.
@@ -21,6 +21,12 @@ dumps, and similar coordination files. These are **work records, not documentati
21
21
  - Knowledge that must outlive the session (real documentation, ADRs, operator
22
22
  guides) is written deliberately into the repository's documentation structure
23
23
  through normal review — not left behind as a work record.
24
+ - **A worktree is a work record too.** Remove the worktree you created for a task
25
+ once its branch is merged to the default branch — pushed is not merged. Do it from
26
+ the parent checkout, after `git status --ignored` in the worktree, because a nested
27
+ `.ai-work/`, `.env` files and local databases are ignored and are deleted with it.
28
+ If the branch must outlive the task unmerged, leave the worktree and say so in the
29
+ handoff. Never `--force` a removal; a refusal means look, not push harder.
24
30
  - **A decision log is not a work record.** A decision — a durable choice with a
25
31
  decider and a date — is documentation: it lives in **`decisions/`** (or the
26
32
  repository's documented decisions folder) and is reviewed like any other
@@ -0,0 +1,34 @@
1
+ ---
2
+ applyTo: "**/*"
3
+ profile: framework
4
+ ---
5
+
6
+ # New Repository Strategy Intake
7
+
8
+ This is Cratis organization process for maintainers creating repositories under the
9
+ Cratis organization. It does not apply to applications built on Cratis.
10
+
11
+ When a repository is created in the Cratis organization, prepare one transient,
12
+ no-effect Strategy intake proposal. Do not create, comment on, assign, mention,
13
+ link, or otherwise mutate a GitHub issue unless a current repository profile and
14
+ the exact operation are separately accepted. Tool access and a request to create
15
+ the repository do not supply that issue-effect authority.
16
+
17
+ The proposal should include only the bounded facts needed for Strategy triage:
18
+
19
+ - repository name, URL, visibility, and creation state;
20
+ - purpose, intended users, lifecycle, and whether it is canonical, generated,
21
+ experimental, operational, or scheduled for retirement;
22
+ - accountable owner or explicit vacancy and cross-repository boundaries;
23
+ - upstream/downstream dependencies and current owning records;
24
+ - release, distribution, credential, security, privacy, compliance, and data
25
+ expectations; and
26
+ - requested Strategy identity, portfolio, metadata, ownership, and local AI
27
+ context review.
28
+
29
+ First decide whether no record, an existing Strategy record, a bounded update
30
+ proposal, a new proposal, or a sensitive human route is appropriate. Treat the
31
+ result as Strategy intake, not strategic approval. Do not invent Strategy
32
+ metadata or copy private Strategy content into a public repository. Repository
33
+ creation does not require an issue URL; unresolved Strategy reconciliation is an
34
+ explicit next action for the authorized human/owning process.
@@ -10,41 +10,27 @@ paths:
10
10
  > **⚠️ APPLIES ONLY TO PROJECTS USING MICROSOFT ORLEANS**
11
11
  > If your project does not reference `Microsoft.Orleans` or any Orleans packages, **ignore this entire file**. These rules are irrelevant outside of Orleans contexts.
12
12
 
13
- These conventions apply to projects that use Microsoft Orleans for distributed grain-based actors.
13
+ In the Cratis organization, Orleans is the Chronicle kernel's actor runtime (`Source/Kernel/`). These conventions describe how that kernel is shaped, verified against `Cratis/Chronicle` **v18.3.0**; follow them in any Cratis repository that hosts Orleans grains.
14
14
 
15
15
  ## General
16
16
 
17
- - Do **not** split Orleans grain interfaces into a separate project. Keep all grain types in the same project as their implementations.
18
- - Use localhost clustering for local development (`UseLocalhostClustering()`).
19
- - Do **not** add `[Alias]` attributes to grain interfaces or methods — rely on the default alias generated by Orleans.
17
+ - Do **not** split grain interfaces into a separate project. Grain interfaces and their implementations live together in `Kernel/Core`; the `Contracts` project holds the gRPC contracts, not grain interfaces.
18
+ - Clustering is chosen by the host, not the grain code: the server uses `UseLocalhostClustering(siloPort, gatewayPort, serviceId, clusterId)` for local development and the MongoDB storage integration switches on `UseMongoDBClustering` (`MongoDBMembershipStrategy.Multiple`) when clustering is enabled. A silo pointed at non-local shared storage while still on localhost clustering is logged as a **warning** at startup (each such node forms its own single-node cluster over the same data); `Cratis__Chronicle__Clustering__Type=MongoDB` on every node is what joins them.
19
+ - Do **not** add `[Alias]` to grain interfaces or grain methods — rely on the default alias Orleans generates. `[Alias(nameof(Type))]` is used only on `[GenerateSerializer]` **state and definition records** in `Kernel/Concepts` (`ReadModelDefinition`, `IndexDefinition`, `ReadModelChangeContext`, `ProjectionFuture`), where a stable serialization alias must survive a rename.
20
20
 
21
- ## Storage Providers
21
+ ## Grain storage providers
22
22
 
23
- - Centralize all storage provider names in a single `WellKnownStorageProviders` static class at the root of the project.
24
- - Reference provider names via the constants in `WellKnownStorageProviders` — never use magic strings.
25
- - Use `WellKnownStorageProviders.Default` as the name for the primary MongoDB-backed storage provider.
23
+ - Storage provider names are constants in `WellKnownGrainStorageProviders` (`Kernel/Core`), **one name per concern** — 25 of them at v18.3.0 (`Namespaces`, `EventSequences`, `ObserverDefinitions`, `ObserverState`, `FailedPartitions`, `Jobs`, `JobSteps`, `Recommendations`, `Projections`, `ProjectionsManager`, `ProjectionFutures`, `Reactors`, `Reducers`, `ReducersManager`, `ReadModels`, `ReadModelsManager`, `ReadModelReplayManager`, `Constraints`, `EventStoreSubscriptionsManager`, `EventSeeding`, `Webhooks`, `WebhooksManager`, `DataProtectionKeys`, `PatchManager`, `System`). Reference a provider only through its constant — never a magic string, and there is no "default" provider.
24
+ - Each concern has its own `IGrainStorage` implementation (`JobGrainStorageProvider`, `EventSequencesStorageProvider`, `NamespacesStateStorageProvider`, …) taking the kernel's `IStorage` abstraction — **not** a MongoDB collection. The storage backend (MongoDB, SQL, in-memory) is selected behind `IStorage`, so a grain storage provider never references a database driver.
25
+ - Every provider is registered once, in `StorageProviderExtensions.AddStorageProviders(this ISiloBuilder)`, as a keyed singleton wrapped in `ResilientGrainStorage` (a Polly resilience pipeline configured from `Chronicle:ResilientStorage`). A new concern adds a constant, an `IGrainStorage` over `IStorage`, and one `AddKeyedSingleton(WellKnownGrainStorageProviders.<Name>, CreateResilientStorageFor<…>)` line there — nothing else.
26
+ - Grains declare their state with `[PersistentState(nameof(<StateType>), WellKnownGrainStorageProviders.<Name>)]`.
26
27
 
27
28
  ```csharp
28
- public static class WellKnownStorageProviders
29
+ public class JobGrainStorageProvider(IStorage storage) : IGrainStorage
29
30
  {
30
- public const string Default = "MongoDbGrainStorage";
31
+ // ReadStateAsync / WriteStateAsync / ClearStateAsync over
32
+ // storage.GetEventStore(key.EventStore).GetNamespace(key.Namespace).Jobs
31
33
  }
32
- ```
33
-
34
- - Annotate grains with `[StorageProvider(ProviderName = WellKnownStorageProviders.Default)]` when they need persistent state.
35
-
36
- ## MongoDB Grain Storage
37
34
 
38
- - The `MongoDbGrainStorage<TState>` implementation accepts `IMongoCollection<TState>` as its only dependency — no additional plumbing is required.
39
- - Register it as a keyed singleton in the Orleans silo builder:
40
-
41
- ```csharp
42
- builder.Host.UseOrleans(siloBuilder =>
43
- {
44
- siloBuilder.UseLocalhostClustering();
45
- siloBuilder.Services.AddKeyedSingleton<IGrainStorage>(
46
- WellKnownStorageProviders.Default,
47
- (sp, _) => new MongoDbGrainStorage<MyState>(
48
- sp.GetRequiredService<IMongoCollection<MyState>>()));
49
- });
35
+ services.AddKeyedSingleton(WellKnownGrainStorageProviders.Jobs, CreateResilientStorageFor<JobGrainStorageProvider>);
50
36
  ```
@@ -1,3 +1,9 @@
1
+ ---
2
+ applyTo: "**/.cratis/**"
3
+ paths:
4
+ - "**/.cratis/**"
5
+ ---
6
+
1
7
  # AI Corpus Profiles
2
8
 
3
9
  Profiles determine which rules and skills apply to your work. They define the scope of the AI corpus and which documentation, conventions, and skills are available.
@@ -8,7 +8,7 @@ paths:
8
8
 
9
9
  # React + Arc + Cratis Components
10
10
 
11
- The frontend is React + TypeScript in MVVM style, composed from **Cratis Components** on top of **Arc-generated proxies**. Cratis Components is PrimeReact-based; you reach PrimeReact almost exclusively through the Cratis wrappers. For component structure, styling, and icons see [components.md](./components.md); for dialogs see [dialogs.md](./dialogs.md). This rule covers MVVM, queries, and commands.
11
+ The frontend is React + TypeScript in MVVM style, composed from **Cratis Components** on top of **Arc-generated proxies**. Cratis Components (4.x) owns its markup, styling contract and behavior — there is no vendor component library underneath to reach for; React Aria is an internal detail you never import. For component structure, styling, and icons see [components.md](./components.md); for dialogs see [dialogs.md](./dialogs.md). This rule covers MVVM, queries, and commands.
12
12
 
13
13
  ## The proxy boundary
14
14
 
@@ -81,7 +81,7 @@ Extract as soon as a component has **any** of: 3+ `useState`, any `useCallback`
81
81
 
82
82
  These are registered automatically by `withViewModel` and injected via the primary constructor. Concrete usage:
83
83
 
84
- - **`IMessenger`** — messages are plain classes; `_messenger.publish(new <Entity>Selected(id))` and `messenger.subscribe(<Entity>Selected, ({ id }) => ...)`. For scoped delivery, the messenger from `@cratis/arc/messaging` (distinct from `…mvvm/messaging`) resolves the nearest scope and falls back to root: `publish()` trickles **down** into nested scopes; bubbling **up** is opt-in with `{ bubble: true }`. Use the default `IMessenger` unless you genuinely need scoping.
84
+ - **`IMessenger`** — messages are plain classes; `_messenger.publish(new <Entity>Selected(id))` and `messenger.subscribe(<Entity>Selected, ({ id }) => ...)`. For scoped delivery, the messenger from `@cratis/arc/messaging` (distinct from `…mvvm/messaging`) resolves the nearest scope and falls back to root: a `<MessengerScope>` trickles `publish()` **down** into nested scopes by default (`trickleDownToChildren`, default `true`) and bubbles **up** only when `bubbleToParent` is set (default `false`) — both are props on `MessengerScope` / properties on the messenger, not options on `publish()`. Use the default `IMessenger` unless you genuinely need scoping.
85
85
  - **`IDialogs`** — `await _dialogs.showConfirmation(title, message, DialogButtons.YesNo)` returns a `DialogResult`. For user-triggered dialogs that open React command dialogs, use `useDialog` in the component, not `IDialogs`.
86
86
  - **`IIdentityProvider`** — `await _identityProvider.getCurrent<TDetails>()`. Use the `useIdentity()` hook only in functional components without a view model.
87
87
  - **`ILocalStorage`** — `setItem(key, value)` / `getItem(key)`; **`IViewModelDetached.detached()`** runs on unmount (`detached() { this._subscription?.unsubscribe(); }`).
@@ -94,7 +94,7 @@ A typed proxy class is generated per query. Pick the hook:
94
94
 
95
95
  | Hook | When |
96
96
  |---|---|
97
- | `<Query>.use(args?, sorting?)` | standard — returns `[result]`, re-renders on change |
97
+ | `<Query>.use(args?, sorting?)` | standard — returns `[result, perform, setSorting]`, re-renders on change |
98
98
  | `<Query>.useSuspense(...)` | suspense-aware — throws while loading; wrap in `<QueryBoundary>` |
99
99
  | `<Query>.when(condition).use(args)` | conditional — only fires when `condition` is true (don't wrap a hook in `if`) |
100
100
  | `<Query>.useWithPaging(pageSize, args?, sorting?)` | server-side paging (backend returns `IQueryable<T>`) |
@@ -106,12 +106,12 @@ A typed proxy class is generated per query. Pick the hook:
106
106
 
107
107
  | Hook | Returns |
108
108
  |---|---|
109
- | `<Query>.use(args?, sorting?)` | `[result, perform]` |
109
+ | `<Query>.use(args?, sorting?)` | `[result, perform, setSorting]` |
110
110
  | `<Query>.useSuspense(...)` | `[result, perform, setSorting]` |
111
111
  | `<Query>.useWithPaging(pageSize, args?, sorting?)` | `[result, perform, setSorting, setPage, setPageSize]` |
112
112
  | `<Query>.useSuspenseWithPaging(pageSize, ...)` | `[result, perform, setSorting, setPage, setPageSize]` |
113
- | `<ObservableQuery>.use(...)` | `[result, setSorting]` — **no `perform`** |
114
- | `<ObservableQuery>.useChangeStream(args?, getKey?, sorting?, isEnabled?)` | `ChangeSet<T> { added, replaced, removed }` |
113
+ | `<ObservableQuery>.use(...)` | `[result, setSorting]` for an enumerable query; `[result]` for a single-instance one — **no `perform`** |
114
+ | `<ObservableQuery>.useChangeStream(args?, getKey?, sorting?)` | `ChangeSet<T> { added, replaced, removed }` |
115
115
 
116
116
  `result.paging` = `{ page, size, totalItems, totalPages }` (zero-based `page`). Read models returning `IQueryable<TReadModel>` get automatic server-side paging/sorting — use it whenever a list can grow.
117
117
 
@@ -141,7 +141,7 @@ Inside dialogs, use `CommandDialog` + CommandForm fields rather than driving the
141
141
  | `isValid` | validation failed — `validationResults` has per-field messages | render inline field errors |
142
142
  | `hasExceptions` | `Provide()`/`Handle()` threw — `exceptionMessages` carry diagnostics | generic error toast + log; never show stack traces |
143
143
 
144
- `CommandDialog` handles all of this automatically; the flags matter most when executing a command **outside** a dialog. `validate()` returns the same shape with `response == null` (handler did not run). `result.validationResults` is a `ValidationResult[]` of `{ property, message }` — render per-field UI from it, but **never branch on raw `.message` text**. `exceptionMessages`/`exceptionStackTrace` are for logging, never for users.
144
+ `CommandDialog` handles all of this automatically; the flags matter most when executing a command **outside** a dialog. `validate()` returns the same shape with `response == null` (handler did not run). `result.validationResults` is a `ValidationResult[]` of `{ severity, message, members: string[], state, reason, reasonDetail? }` — `members` names the affected properties — render per-field UI from it, but **never branch on raw `.message` text**. `exceptionMessages`/`exceptionStackTrace` are for logging, never for users.
145
145
 
146
146
  Out-of-dialog execution branches in this order:
147
147
 
@@ -157,11 +157,11 @@ if (result.hasExceptions) { toast.error('Something went wrong'); console.error(r
157
157
 
158
158
  ### Command helpers
159
159
 
160
- - **`useCommandInstance(Command)`** — read the live reactive command instance for dependent fields (read it; never mutate — mutations go through field bindings).
160
+ - **`useCommandInstance<TCommand>()`** — inside a `CommandForm`/`CommandDialog`, read the live reactive command instance from context for dependent fields (no argument; the type parameter is the command type) (read it; never mutate — mutations go through field bindings).
161
161
  - **`onBeforeExecute`** is a **transformer**: it receives the current values and **must return them** (mutated or not). It runs only on submit — never use it to seed required values (use `initialValues`).
162
162
  - **`initialValues`** = synchronous baseline (also drives change tracking); **`currentValues`** = reactive overlay for async/late-loading values (e.g. from a query).
163
163
  - **`asCommandFormField<P>(Component, opts)`** — wrap a custom input (rich text, address picker) so it participates in `CommandForm` with validation/error wiring.
164
- - **`CommandScope`** + `useCommandScope()` (`@cratis/arc.react/commands`) — aggregate state across multiple commands (and queries) on one screen; members tracked automatically (no `addCommand`); scopes nest (a child reports up to its parent); injectable as `ICommandScope` (nearest enclosing scope). Props: `setHasChanges`, `setIsPerforming`, `onBeforeExecute`, `onSuccess(cmd, result)`, `onFailed`, `onException`, `onUnauthorized`. `ICommandScope` members: `hasChanges`, `isPerforming`, `hasValidationFailures`, `hasExceptions`, `validationFailures`/`exceptions` (this scope), `aggregatedValidationFailures`/`aggregatedExceptions` (scope + children), `execute()` (runs only commands with changes), `revertChanges()`, `parent`.
164
+ - **`CommandScope`** + `useCommandScope()` (`@cratis/arc.react/commands`) — aggregate state across multiple commands (and queries) on one screen; commands and queries rendered inside the scope register themselves (the `ICommandScope` API also exposes `addCommand`/`addQuery`/`addChildScope` for manual registration); scopes nest (a child reports up to its parent); injectable as `ICommandScope` (nearest enclosing scope). Props: `setHasChanges`, `setIsPerforming`, `onBeforeExecute`, `onSuccess(cmd, result)`, `onFailed`, `onException`, `onUnauthorized`. `ICommandScope` members: `hasChanges`, `isPerforming`, `hasValidationFailures`, `hasExceptions`, `validationFailures`/`exceptions` (this scope), `aggregatedValidationFailures`/`aggregatedExceptions` (scope + children), `execute()` (runs only commands with changes), `revertChanges()`, `parent`.
165
165
 
166
166
  ### `<Arc>` global configuration
167
167
 
@@ -175,7 +175,7 @@ Top-level provider for microservice name and API base path. Config props (with d
175
175
  | `queryConnectionCount` | `1` | hub connection slots |
176
176
  | `queryDirectMode` | `false` | direct per-query connection — dev use only |
177
177
 
178
- `QueryTransportMethod`/`ObservableQueryTransferMode` import from `@cratis/arc`; `Arc` from `@cratis/arc.react`. Reach `arc.reconnectQueries()` via `useContext(ArcContext)` and call it after login/logout to re-establish observable connections.
178
+ `QueryTransportMethod` imports from `@cratis/arc/queries` and `ObservableQueryTransferMode` from `@cratis/arc`; `Arc` from `@cratis/arc.react`. Reach `arc.reconnectQueries()` via `useContext(ArcContext)` and call it after login/logout to re-establish observable connections.
179
179
 
180
180
  ## Composition
181
181
 
@@ -185,11 +185,11 @@ Top-level provider for microservice name and API base path. Config props (with d
185
185
 
186
186
  ## Styling default
187
187
 
188
- Default to **Cratis Components on PrimeReact theming/tokens** (`var(--surface-*)`, `var(--primary-color)`, `var(--text-color)`, `pt`/unstyled where needed) — see [components.md](./components.md). Tailwind is **not** the Cratis default (it's one supported unstyled path). Never hard-code hex/`rgb()` for chrome.
188
+ Default to **Cratis Components with the `--cratis-*` tokens** (`var(--cratis-surface-*)`, `var(--cratis-primary-color)`, `var(--cratis-text-color)`; `pt`/`data-cratis-part` when you must reach a part) — see [components.md](./components.md). Tailwind is **not** the Cratis default: it is one supported way to *write* the CSS that maps a product's design onto those tokens. Never hard-code hex/`rgb()` for chrome.
189
189
 
190
190
  ## See also
191
191
 
192
- - [components.md](./components.md) — component structure, CSS, PrimeReact tokens, icons, Storybook.
192
+ - [components.md](./components.md) — component structure, CSS, `--cratis-*` tokens and parts, icons, Storybook.
193
193
  - [dialogs.md](./dialogs.md) — `CommandDialog` / `Dialog` / `StepperCommandDialog` rules.
194
194
  - [frontend-quality.md](./frontend-quality.md) — engineering bar; [frontend-testing.md](./frontend-testing.md) — BDD specs.
195
195
  - [typescript.md](./typescript.md) — TS style.