@cratis/pi 2.22.0 → 2.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/package/corpus/agents/backend-developer.md +6 -5
  2. package/package/corpus/agents/code-reviewer.md +7 -7
  3. package/package/corpus/agents/coordinator.md +5 -4
  4. package/package/corpus/agents/frontend-developer.md +34 -40
  5. package/package/corpus/agents/orchestrator.md +5 -4
  6. package/package/corpus/agents/performance-reviewer.md +5 -4
  7. package/package/corpus/agents/planner.md +5 -4
  8. package/package/corpus/agents/repository-investigation-reviewer.md +2 -1
  9. package/package/corpus/agents/repository-investigator.md +2 -1
  10. package/package/corpus/agents/security-reviewer.md +5 -4
  11. package/package/corpus/agents/slice-implementer.md +8 -2
  12. package/package/corpus/agents/spec-writer.md +6 -4
  13. package/package/corpus/harnesses/pi/extensions/subagent/agents.ts +52 -7
  14. package/package/corpus/harnesses/pi/extensions/subagent/index.ts +14 -0
  15. package/package/corpus/hooks/README.md +29 -27
  16. package/package/corpus/hooks/agent-stop.md +1 -5
  17. package/package/corpus/hooks/scripts/cratis-patterns.json +1 -1
  18. package/package/corpus/hooks/scripts/validate-package-subpaths.sh +1 -2
  19. package/package/corpus/prompts/verify-ai-setup.prompt.md +17 -7
  20. package/package/corpus/rules/ai-distribution.md +58 -0
  21. package/package/corpus/rules/components.md +77 -44
  22. package/package/corpus/rules/dialogs.md +24 -20
  23. package/package/corpus/rules/efcore.md +35 -187
  24. package/package/corpus/rules/framework.md +1 -1
  25. package/package/corpus/rules/frontend-quality.md +1 -1
  26. package/package/corpus/rules/frontend-testing.md +4 -4
  27. package/package/corpus/rules/general.md +29 -105
  28. package/package/corpus/rules/new-repository-intake.md +34 -0
  29. package/package/corpus/rules/orleans.md +13 -27
  30. package/package/corpus/rules/react.md +12 -12
  31. package/package/corpus/rules/reactors.md +28 -5
  32. package/package/corpus/rules/specs.scenarios.csharp.md +18 -3
  33. package/package/corpus/rules/specs.typescript.md +1 -1
  34. package/package/corpus/rules/storybook.md +2 -2
  35. package/package/corpus/rules/typescript.md +1 -0
  36. package/package/corpus/rules/vertical-slices.md +20 -15
  37. package/package/corpus/rules/writing-correct-examples.md +1 -0
  38. package/package/corpus/skills/cratis-application-react-specifications/SKILL.md +4 -4
  39. package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +16 -8
  40. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +6 -4
  41. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +1 -1
  42. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +1 -1
  43. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +1 -1
  44. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +1 -1
  45. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +7 -4
  46. package/package/corpus/skills/cratis-arc-command/SKILL.md +9 -7
  47. package/package/corpus/skills/cratis-arc-command/references/command-result.md +1 -1
  48. package/package/corpus/skills/cratis-arc-command/references/handler-shapes.md +2 -2
  49. package/package/corpus/skills/cratis-arc-command/references/proxy-generation.md +6 -3
  50. package/package/corpus/skills/cratis-arc-command/references/read-model-injection.md +1 -1
  51. package/package/corpus/skills/cratis-arc-command-execution/SKILL.md +12 -2
  52. package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +5 -3
  53. package/package/corpus/skills/cratis-arc-ef-core-migration/SKILL.md +7 -4
  54. package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +10 -3
  55. package/package/corpus/skills/cratis-arc-query-paging/SKILL.md +13 -8
  56. package/package/corpus/skills/cratis-arc-react-page/SKILL.md +27 -24
  57. package/package/corpus/skills/cratis-arc-react-page/references/data-page.md +15 -11
  58. package/package/corpus/skills/cratis-arc-react-page/references/data-tables.md +9 -2
  59. package/package/corpus/skills/cratis-arc-react-page/references/dialogs.md +11 -9
  60. package/package/corpus/skills/cratis-arc-react-page/references/queries-and-commands.md +26 -2
  61. package/package/corpus/skills/cratis-chronicle-cli-operations/SKILL.md +19 -13
  62. package/package/corpus/skills/cratis-chronicle-client-dotnet/SKILL.md +17 -15
  63. package/package/corpus/skills/cratis-chronicle-client-elixir/SKILL.md +24 -20
  64. package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +26 -15
  65. package/package/corpus/skills/cratis-chronicle-client-typescript/SKILL.md +15 -11
  66. package/package/corpus/skills/cratis-chronicle-compliance/SKILL.md +14 -3
  67. package/package/corpus/skills/cratis-chronicle-event-constraints/SKILL.md +13 -6
  68. package/package/corpus/skills/cratis-chronicle-event-modeling/SKILL.md +2 -2
  69. package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +11 -5
  70. package/package/corpus/skills/cratis-chronicle-event-type-migration/SKILL.md +2 -2
  71. package/package/corpus/skills/cratis-chronicle-multi-tenancy/SKILL.md +2 -2
  72. package/package/corpus/skills/cratis-chronicle-projection/SKILL.md +23 -4
  73. package/package/corpus/skills/cratis-chronicle-projection/references/fluent-builder.md +1 -1
  74. package/package/corpus/skills/cratis-chronicle-projection/references/model-bound-attributes.md +2 -2
  75. package/package/corpus/skills/cratis-chronicle-reactor/SKILL.md +22 -8
  76. package/package/corpus/skills/cratis-chronicle-read-model/SKILL.md +1 -1
  77. package/package/corpus/skills/cratis-chronicle-read-model/references/queries.md +1 -1
  78. package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +3 -1
  79. package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +1 -1
  80. package/package/corpus/skills/cratis-chronicle-web-workbench/SKILL.md +19 -11
  81. package/package/corpus/skills/cratis-cli-terminal-workbench/SKILL.md +11 -6
  82. package/package/corpus/skills/cratis-code-review/SKILL.md +8 -6
  83. package/package/corpus/skills/cratis-components-accessibility/SKILL.md +112 -82
  84. package/package/corpus/skills/cratis-components-schema-editor/SKILL.md +12 -10
  85. package/package/corpus/skills/cratis-components-stepper-command-dialog/SKILL.md +11 -12
  86. package/package/corpus/skills/cratis-components-styling/SKILL.md +204 -170
  87. package/package/corpus/skills/cratis-components-toolbar/SKILL.md +32 -26
  88. package/package/corpus/skills/cratis-event-model-diagram/SKILL.md +1 -1
  89. package/package/corpus/skills/cratis-fundamentals-concept/SKILL.md +2 -2
  90. package/package/corpus/skills/cratis-fundamentals-type-discovery/SKILL.md +2 -2
  91. package/package/corpus/skills/cratis-lens-browser-extension/SKILL.md +4 -2
  92. package/package/corpus/skills/cratis-performance-review/SKILL.md +4 -2
  93. package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +15 -5
  94. package/package/corpus/skills/cratis-security-review/SKILL.md +20 -3
  95. package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +3 -3
  96. package/package/corpus/skills/cratis-specifications-csharp/references/application-scenarios.md +12 -8
  97. package/package/corpus/skills/cratis-specifications-csharp/references/csharp-patterns.md +1 -1
  98. package/package/corpus/skills/cratis-specifications-csharp/references/integration-specs.md +7 -3
  99. package/package/corpus/skills/cratis-specifications-typescript/SKILL.md +6 -3
  100. package/package/corpus/skills/cratis-specifications-typescript/references/typescript-patterns.md +21 -4
  101. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +29 -17
  102. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  # Tenancy
2
2
 
3
- Verified against `Cratis.Arc.Core` `22.10.4`. Types are in `Cratis.Arc.Tenancy`.
3
+ Verified against `Cratis.Arc.Core` `22.16.0`. Types are in `Cratis.Arc.Tenancy`.
4
4
 
5
5
  Arc tenancy is **request-scoped tenant resolution**. It answers "which tenant is
6
6
  this request for". It is not Chronicle's tenant namespace isolation, and it does
@@ -61,9 +61,12 @@ the configured value.
61
61
 
62
62
  `ITenantIdAccessor.Current` gives the current `TenantId`, cached into an
63
63
  `AsyncLocal`. An empty resolution is `TenantId.NotSet`, whose value is the
64
- literal string `"[NotSet]"`. `TenantId.Default` is `"Default"` and `IsDefault`
65
- tests for it. `CurrentTenantIdIsNotSet.ThrowIfNotSet(...)` is the guard for code
66
- that requires a tenant.
64
+ literal string `"[NotSet]"`. `TenantId.Default` is `"Default"`. ⚠️ `IsDefault`
65
+ is `this == NotSet || this == Default` — it is true for an **unresolved** tenant
66
+ as well, so it cannot tell "single-tenant default" from "nothing resolved".
67
+ Compare against `TenantId.NotSet` explicitly, or use
68
+ `CurrentTenantIdIsNotSet.ThrowIfNotSet(...)`, wherever an unresolved tenant must
69
+ be refused.
67
70
 
68
71
  ⚠️ `NotSet` is a real string, not `null`. A check like
69
72
  `string.IsNullOrEmpty(tenantId)` never fires for an unresolved tenant. Compare
@@ -14,12 +14,14 @@ whatever `Handle()` returns into appended events, a response, or both.
14
14
 
15
15
  | Package | Version | Purpose |
16
16
  | --- | --- | --- |
17
- | `Cratis.Arc.Core` | `22.10.4` | `Cratis.Arc.Commands.ModelBound.CommandAttribute`, `CommandResult`, `CommandValidator<T>` |
18
- | `Cratis.Arc.Chronicle` | `22.10.4` | event append handling, `Cratis.Arc.Chronicle.Commands.NotAuditedAttribute` |
19
- | `Cratis.Arc.ProxyGenerator.Build` | `22.10.4` | `CratisProxiesOutputPath` MSBuild integration |
20
- | `Cratis.Fundamentals` | `7.18.2` | `Cratis.Monads.Result<TResult, TError>` |
21
- | `Cratis.Chronicle` | `16.39.1` | `EventTypeAttribute`, `EventForEventSourceId`, `ICanProvideEventSourceId` |
22
- | `@cratis/arc` | `22.10.4` | `ICommand`, `CommandResult`, `ValidationResult` |
17
+ | `Cratis.Arc.Core` | `22.16.0` | `Cratis.Arc.Commands.ModelBound.CommandAttribute`, `CommandResult`, `CommandValidator<T>` |
18
+ | `Cratis.Arc.Chronicle` | `22.16.0` | event append handling, `Cratis.Arc.Chronicle.Commands.NotAuditedAttribute` |
19
+ | `Cratis.Arc.ProxyGenerator.Build` | `22.16.0` | `CratisProxiesOutputPath` MSBuild integration |
20
+ | `Cratis.Fundamentals` | `7.19.2` | `Cratis.Monads.Result<TResult, TError>` |
21
+ | `Cratis.Chronicle` | `18.3.0` | `EventTypeAttribute`, `EventForEventSourceId`, `ICanProvideEventSourceId` |
22
+ | `@cratis/arc` | `22.16.0` | `ICommand`, `CommandResult`, `ValidationResult` |
23
+
24
+ > Re-verified at the versions above by **symbol and signature**: every type, attribute and member this skill names exists at that tag, and the public surface it describes is unchanged since the previous verification (Chronicle 16.45.x / Arc 22.10.4 — the Chronicle 16→18 client diff is converters, options and doc comments; no type was removed or renamed). Behavior claims were verified at the earlier tag unless a section says otherwise.
23
25
 
24
26
  Reverify before claiming support for another version. Arc without Chronicle is a
25
27
  supported setup; everything on this page that appends events needs Chronicle.
@@ -214,7 +216,7 @@ either way, so the reading is all a reviewer has to go on.
214
216
  ## Generate the TypeScript proxy
215
217
 
216
218
  ```xml
217
- <PackageReference Include="Cratis.Arc.ProxyGenerator.Build" Version="22.10.4" />
219
+ <PackageReference Include="Cratis.Arc.ProxyGenerator.Build" Version="22.16.0" />
218
220
 
219
221
  <PropertyGroup>
220
222
  <CratisProxiesOutputPath>$(MSBuildThisFileDirectory)../<Web>/src/api</CratisProxiesOutputPath>
@@ -1,6 +1,6 @@
1
1
  # CommandResult and ValidationResult
2
2
 
3
- Verified against `Cratis.Arc.Core` `22.10.4` and `@cratis/arc` `22.10.4`.
3
+ Verified against `Cratis.Arc.Core` `22.16.0` and `@cratis/arc` `22.16.0`.
4
4
 
5
5
  ## Server side
6
6
 
@@ -1,7 +1,7 @@
1
1
  # Handler shapes and how Arc dispatches them
2
2
 
3
- Verified against `Cratis.Arc.Core` and `Cratis.Arc.Chronicle` `22.10.4` with
4
- `Cratis.Chronicle` `16.39.1`.
3
+ Verified against `Cratis.Arc.Core` and `Cratis.Arc.Chronicle` `22.16.0` with
4
+ `Cratis.Chronicle` `18.3.0`.
5
5
 
6
6
  ## The pipeline around `Handle`
7
7
 
@@ -1,6 +1,6 @@
1
1
  # Proxy generation
2
2
 
3
- Verified against `Cratis.Arc.ProxyGenerator.Build` `22.10.4`.
3
+ Verified against `Cratis.Arc.ProxyGenerator.Build` `22.16.0`.
4
4
 
5
5
  ## Wiring
6
6
 
@@ -8,7 +8,7 @@ Reference the build package from the project that contains the commands,
8
8
  queries and read models, and set an output path:
9
9
 
10
10
  ```xml
11
- <PackageReference Include="Cratis.Arc.ProxyGenerator.Build" Version="22.10.4" />
11
+ <PackageReference Include="Cratis.Arc.ProxyGenerator.Build" Version="22.16.0" />
12
12
 
13
13
  <PropertyGroup>
14
14
  <CratisProxiesOutputPath>$(MSBuildThisFileDirectory)../<Web>/src/api</CratisProxiesOutputPath>
@@ -27,7 +27,7 @@ Output folders mirror the C# **namespace**, not the file path. A command in
27
27
 
28
28
  ## MSBuild properties
29
29
 
30
- Every property below is declared by the package with the default shown.
30
+ The first nine properties below are declared with the default shown in the package's `.props`; the last three are read by the `.targets` only when set to `true` (absent means off).
31
31
 
32
32
  | Property | Default | Effect |
33
33
  | --- | --- | --- |
@@ -40,6 +40,9 @@ Every property below is declared by the package with the default shown.
40
40
  | `CratisProxiesSkipQueryNameInRoute` | `false` | Leaves the query name out of the generated route |
41
41
  | `CratisProxiesApiPrefix` | `api` | Route prefix |
42
42
  | `CratisProxiesSkipFileIndexTracking` | `false` | Disables tracking which files the generator owns |
43
+ | `CratisProxiesSkipIndexGeneration` | `false` | Does not write the per-folder `index.ts` barrels |
44
+ | `CratisProxiesUseSourceFileAsOutputFile` | `false` | Names the generated file after the C# source file rather than the type |
45
+ | `CratisProxiesEmitInterfaces` | off | **Arc ≥ 22.15.0** (the underlying `--emit-interfaces` flag exists from 22.14.0; the MSBuild property from 22.15.0). Emits plain TypeScript `interface`s instead of `@field`-decorated classes — no `@cratis/fundamentals` import in the output. Only for consumers that never reconstruct instances from JSON; command and query proxies need the classes |
43
46
 
44
47
  ⚠️ `CratisProxiesSkipOutputDeletion` defaults to `true`. Guidance that tells you
45
48
  to set it to `true` "so the generator does not delete your hand-written files"
@@ -1,6 +1,6 @@
1
1
  # Injecting a read model into a command
2
2
 
3
- Verified against `Cratis.Arc.Core` `22.10.4` and `Cratis.Chronicle` `16.39.1`.
3
+ Verified against `Cratis.Arc.Core` `22.16.0` and `Cratis.Chronicle` `18.3.0`.
4
4
 
5
5
  A read model can be injected into a `CommandValidator<T>` constructor, into
6
6
  `Provide()`, or into `Handle()`. Arc resolves it through an
@@ -15,8 +15,10 @@ dispatch.
15
15
 
16
16
  | Package | Version | Purpose |
17
17
  | --- | --- | --- |
18
- | `Cratis.Arc.Core` | `22.10.4` | `Cratis.Arc.Commands.ICommandPipeline`, `CommandResult`, `ValidationResultSeverity` |
19
- | `Cratis.Arc.Chronicle` | `22.10.4` | analyzer `ARCCHR0006` for reactor replay |
18
+ | `Cratis.Arc.Core` | `22.16.0` | `Cratis.Arc.Commands.ICommandPipeline`, `CommandResult`, `ValidationResultSeverity` |
19
+ | `Cratis.Arc.Chronicle` | `22.16.0` | analyzer `ARCCHR0006` for reactor replay |
20
+
21
+ > Re-verified at the versions above by **symbol and signature**: every type, attribute and member this skill names exists at that tag, and the public surface it describes is unchanged since the previous verification (Chronicle 16.45.x / Arc 22.10.4 — the Chronicle 16→18 client diff is converters, options and doc comments; no type was removed or renamed). Behavior claims were verified at the earlier tag unless a section says otherwise.
20
22
 
21
23
  Reverify before claiming support for another version.
22
24
 
@@ -29,6 +31,14 @@ inject `IEventLog` (`ARCCHR0007`).
29
31
 
30
32
  ## Inject and execute
31
33
 
34
+ > **Reactors have a declarative alternative.** With the Arc Chronicle integration a
35
+ > reactor handler can **return** the `[Command]` (or a collection of commands) and
36
+ > Arc executes it; a denied or invalid result fails the partition instead of being
37
+ > discarded, and `[ExecuteCommandsAsSystem]` on the reactor class supplies a
38
+ > principal for those returned commands. That attribute does **not** cover manual
39
+ > `Execute` calls — the form below needs its own execution context. See the
40
+ > **cratis-chronicle-reactor** skill.
41
+
32
42
  ```csharp
33
43
  public class <Name>Reactor(ICommandPipeline pipeline) : IReactor
34
44
  {
@@ -23,11 +23,13 @@ rejection on it.)
23
23
 
24
24
  | Package | Version | Purpose |
25
25
  | --- | --- | --- |
26
- | `Cratis.Arc.Core` | `22.10.4` | `CommandValidator<T>`, `ConceptValidator<T>`, `ValidationResult`, `CommandResult`, the command pipeline, `ARC0004`–`ARC0013` |
27
- | `Cratis.Fundamentals` | `7.18.2` | `Cratis.Monads.Result<TResult, TError>` |
28
- | `Cratis.Chronicle` | `16.45.2` | Read-model resolution for command-side injection |
26
+ | `Cratis.Arc.Core` | `22.16.0` | `CommandValidator<T>`, `ConceptValidator<T>`, `ValidationResult`, `CommandResult`, the command pipeline, `ARC0004`–`ARC0013` |
27
+ | `Cratis.Fundamentals` | `7.19.2` | `Cratis.Monads.Result<TResult, TError>` |
28
+ | `Cratis.Chronicle` | `18.3.0` | Read-model resolution for command-side injection |
29
29
  | `FluentValidation` | `12.1.1` | `RuleFor(...)`, `.WithMessage(...)` |
30
30
 
31
+ > Re-verified at the versions above by **symbol and signature**: every type, attribute and member this skill names exists at that tag, and the public surface it describes is unchanged since the previous verification (Chronicle 16.45.x / Arc 22.10.4 — the Chronicle 16→18 client diff is converters, options and doc comments; no type was removed or renamed). Behavior claims were verified at the earlier tag unless a section says otherwise.
32
+
31
33
  Reverify against the owning product repository before claiming support for
32
34
  another version.
33
35
 
@@ -15,10 +15,12 @@ Everything else is ordinary EF Core.
15
15
 
16
16
  | Package | Version | Purpose |
17
17
  | --- | --- | --- |
18
- | `Cratis.Arc.EntityFrameworkCore` | `22.10.4` | `BaseDbContext`, `ReadOnlyDbContext`, `WithEntityFrameworkCore`, column helpers, `EntityFrameworkReadModelForCommandResolver` |
19
- | `Cratis.Arc.Core` | `22.10.4` | `Cratis.Arc.Queries.ModelBound.ReadModelAttribute`, `ICanResolveReadModelForCommand` |
18
+ | `Cratis.Arc.EntityFrameworkCore` | `22.16.0` | `BaseDbContext`, `ReadOnlyDbContext`, `WithEntityFrameworkCore`, column helpers, `EntityFrameworkReadModelForCommandResolver` |
19
+ | `Cratis.Arc.Core` | `22.16.0` | `Cratis.Arc.Queries.ModelBound.ReadModelAttribute`, `ICanResolveReadModelForCommand` |
20
20
  | `Microsoft.EntityFrameworkCore` | `10.0.11` | EF Core itself, including all migration tooling |
21
21
 
22
+ > Re-verified at the versions above by **symbol and signature**: every type, attribute and member this skill names exists at that tag, and the public surface it describes is unchanged since the previous verification (Chronicle 16.45.x / Arc 22.10.4 — the Chronicle 16→18 client diff is converters, options and doc comments; no type was removed or renamed). Behavior claims were verified at the earlier tag unless a section says otherwise.
23
+
22
24
  Reverify before claiming support for another version.
23
25
 
24
26
  ⚠️ **Arc ships no migration tooling of its own.** There is no design-time
@@ -148,8 +150,9 @@ migrationBuilder.CreateTable(
148
150
  `StringColumn`, `NumberColumn<T>`, `BoolColumn`, `AutoIncrementColumn`,
149
151
  `GuidColumn` and `DateTimeOffsetColumn` all take the `MigrationBuilder` as their
150
152
  first argument — that is how they learn which database they are generating for.
151
- All of them default `nullable` to **`true`**, so a required column has to say
152
- `nullable: false` explicitly.
153
+ All of them except `AutoIncrementColumn` default `nullable` to **`true`**, so a
154
+ required column has to say `nullable: false` explicitly; `AutoIncrementColumn`
155
+ has no `nullable` parameter and is always `NOT NULL`.
153
156
 
154
157
  On `MigrationBuilder`, for an existing table:
155
158
 
@@ -13,7 +13,9 @@ request is made. Choose the transport first, then the command.
13
13
 
14
14
  | Package | Version | Purpose |
15
15
  | --- | --- | --- |
16
- | `Cratis.Arc.Core` | `22.10.4` | `ObservableQueryHttp`, `ObservableQueryHandler`, the demultiplexer routes |
16
+ | `Cratis.Arc.Core` | `22.16.0` | `ObservableQueryHttp`, `ObservableQueryHandler`, the demultiplexer routes |
17
+
18
+ > Re-verified at the versions above by **symbol and signature**: every type, attribute and member this skill names exists at that tag, and the public surface it describes is unchanged since the previous verification (Chronicle 16.45.x / Arc 22.10.4 — the Chronicle 16→18 client diff is converters, options and doc comments; no type was removed or renamed). Behavior claims were verified at the earlier tag unless a section says otherwise.
17
19
 
18
20
  Reverify before claiming support for another version. This skill is read-only
19
21
  inspection of an endpoint the user already has; it neither implements the query
@@ -29,7 +31,12 @@ handler decides in this order:
29
31
  (case-insensitive);
30
32
  3. **plain HTTP** — everything else, answered once and closed.
31
33
 
32
- A plain `curl` sends neither, so it always lands on the third case.
34
+ A plain `curl` sends neither, so it always lands on the third case — and there
35
+ the two result shapes part ways: an **`ISubject<T>`** query answers with one
36
+ `QueryResult` as described below, while an **`IAsyncEnumerable<T>`** query
37
+ answers **HTTP 400** with `{ "message": "AsyncEnumerable queries require WebSocket connection" }`
38
+ and never a `QueryResult`. Everything that follows on this page is about
39
+ `ISubject<T>`; inspect an `IAsyncEnumerable<T>` query over WebSocket.
33
40
 
34
41
  ## Snapshot: the current value, once
35
42
 
@@ -37,7 +44,7 @@ A plain `curl` sends neither, so it always lands on the third case.
37
44
  curl "https://<host>/<api-prefix>/<route>"
38
45
  ```
39
46
 
40
- The response is one JSON `QueryResult`.
47
+ The response is one JSON `QueryResult` (for an `ISubject<T>` query — see above).
41
48
 
42
49
  ⚠️ **When the observable has not produced its first value yet, this returns HTTP
43
50
  202 Accepted with a not-ready result** — no exception, no data. That is a normal
@@ -13,10 +13,12 @@ the query string — you choose a **return shape** that the framework can narrow
13
13
 
14
14
  | Package | Version | Purpose |
15
15
  | --- | --- | --- |
16
- | `Cratis.Arc.Core` | `22.10.4` | `QueryableQueryRenderer`, `Paging`, `Sorting`, `PagingInfo` |
17
- | `Cratis.Arc.MongoDB` | `22.10.4` | `Observe` helpers that page at the source |
18
- | `Cratis.Arc.EntityFrameworkCore` | `22.10.4` | `DbSet<T>` observe helpers that page at the source |
19
- | `@cratis/arc.react` | `22.10.4` | `useWithPaging`, `useSuspenseWithPaging` |
16
+ | `Cratis.Arc.Core` | `22.16.0` | `QueryableQueryRenderer`, `Paging`, `Sorting`, `PagingInfo` |
17
+ | `Cratis.Arc.MongoDB` | `22.16.0` | `Observe` helpers that page at the source |
18
+ | `Cratis.Arc.EntityFrameworkCore` | `22.16.0` | `DbSet<T>` observe helpers that page at the source |
19
+ | `@cratis/arc.react` | `22.16.0` | `useWithPaging`, `useSuspenseWithPaging` |
20
+
21
+ > Re-verified at the versions above by **symbol and signature**: every type, attribute and member this skill names exists at that tag, and the public surface it describes is unchanged since the previous verification (Chronicle 16.45.x / Arc 22.10.4 — the Chronicle 16→18 client diff is converters, options and doc comments; no type was removed or renamed). Behavior claims were verified at the earlier tag unless a section says otherwise.
20
22
 
21
23
  Reverify before claiming support for another version. A small, bounded result
22
24
  set does not need any of this — `IEnumerable<T>` is fine.
@@ -25,7 +27,7 @@ set does not need any of this — `IEnumerable<T>` is fine.
25
27
 
26
28
  | Query-string key | Effect |
27
29
  | --- | --- |
28
- | `pageSize` | **Required to page at all.** Must be greater than 0 |
30
+ | `pageSize` | **Required to page at all** on the query string; any parseable integer is accepted (`0` yields an empty page, negatives are clamped by `Skip`) — the JSON body reader, by contrast, treats `pageSize <= 0` as not paged |
29
31
  | `page` | Zero-based. Defaults to `0` when `pageSize` is present |
30
32
  | `sortby` | Field name; PascalCased before use, so `?sortby=name` sorts on `Name` |
31
33
  | `sortDirection` | `desc` (case-insensitive) sorts descending; **anything else, including a missing value, is ascending** |
@@ -36,9 +38,12 @@ Two consequences that surprise people:
36
38
  - **Sorting needs both keys.** `sortby` without `sortDirection` is ignored
37
39
  entirely.
38
40
 
39
- Paging is validated before the query runs: `page` must be `>= 0` and `pageSize`
40
- must be `> 0`, and a violation comes back as a validation failure rather than a
41
- crash.
41
+ Paging is **not** validated on the request path at 22.16.0: `PageNumber`/`PageSize`
42
+ concept validators exist (`page >= 0`, `pageSize > 0`) but nothing feeds the
43
+ query-string paging into them, so an out-of-range value produces an empty or
44
+ clamped page rather than a validation failure. Validate paging arguments in the
45
+ frontend, or expose them as explicit query arguments if the rule must be
46
+ enforced.
42
47
 
43
48
  ## Which return shapes page
44
49
 
@@ -15,21 +15,20 @@ This skill is verified against these exact package contracts:
15
15
 
16
16
  | Package | Version | Verified from |
17
17
  | --- | --- | --- |
18
- | `@cratis/components` | `3.0.0` | its package manifest and component sources |
19
- | `@cratis/arc` | `>=20.3.1 <23` | peer range declared by `@cratis/components@3.0.0` |
20
- | `@cratis/arc.react` | `>=20.3.1 <23` | peer range declared by `@cratis/components@3.0.0` |
21
- | `primereact` | `^11.0.0` | peer of `@cratis/components@3.0.0` |
22
- | `primeicons` | `^8.0.0` | peer of `@cratis/components@3.0.0` |
23
- | `react` | `^19.0.0` | peer of `@cratis/components@3.0.0` |
18
+ | `@cratis/components` | `4.6.0` | its package manifest (`exports`, peers), `Source/DataPage/*`, `Source/DataTables/*`, `Source/Dialogs/*`, `Source/CommandDialog/*`, `Source/MIGRATION.md` |
19
+ | `@cratis/arc` | `>=20.3.1 <23` | peer range declared by `@cratis/components@4.6.0` |
20
+ | `@cratis/arc.react` | `>=20.3.1 <23` | peer range declared by `@cratis/components@4.6.0` |
21
+ | `react` | `^19.0.0` | peer of `@cratis/components@4.6.0` |
24
22
 
25
23
  `@cratis/arc.react.mvvm` ships with `@cratis/arc.react` and is an explicit
26
24
  application dependency. Reverify before claiming another version.
27
25
 
28
26
  ## Import from subpaths, never the root barrel
29
27
 
30
- The root `@cratis/components` entry exports **namespaces**, not components —
31
- `import { DataPage } from '@cratis/components'` yields a namespace object whose
32
- component is `DataPage.DataPage`. Always import from the subpath:
28
+ The root `@cratis/components` entry is **setup-only** — `CratisComponentsProvider`
29
+ and its configuration types. `import { DataPage } from '@cratis/components'` is a
30
+ compile error; the Components 3 root namespaces are gone. Always import from the
31
+ subpath:
33
32
 
34
33
  ```tsx
35
34
  import { DataPage, MenuItem, Column } from '@cratis/components/DataPage';
@@ -45,18 +44,21 @@ import { DialogProps, DialogResult, useDialog } from '@cratis/arc.react/dialogs'
45
44
  regenerated the TypeScript proxies. Generated proxies carry a
46
45
  `**DO NOT EDIT** - This file is an automatically generated file.` header —
47
46
  fix the C# source and rebuild instead of editing one.
48
- - The app must mount `CratisComponentsProvider` (from
49
- `@cratis/components/Common`) above every Cratis component. PrimeReact 11
50
- resolves its configuration from a provider, so components fail without one.
51
- - The app must import the stylesheets explicitly; components no longer import
47
+ - The app must mount `CratisComponentsProvider` (from the package root,
48
+ `@cratis/components`) above every Cratis component — it carries the locale,
49
+ the Components-owned labels and the toast region (`toaster`).
50
+ - The app must import the stylesheets explicitly; components do not import
52
51
  their own CSS:
53
52
 
54
53
  ```ts
55
- import '@cratis/components/tokens'; // the --cratis-* token layer
56
- import '@cratis/components/styles'; // every component stylesheet
57
- import '@cratis/components/theme'; // optional license-free baseline look
54
+ import '@cratis/components/tokens'; // the --cratis-* token seam
55
+ import '@cratis/components/styles'; // structural rules for every component
56
+ import '@cratis/components/theme'; // optional: the maintained baseline look
58
57
  ```
59
58
 
59
+ Components has no vendor UI dependency — do not install PrimeReact or an icon
60
+ font for it. Menu icons are React component types (`react-icons` works).
61
+
60
62
  See the **cratis-components-styling** skill for the full theming contract.
61
63
 
62
64
  ## Step 2 — The DataPage shell
@@ -81,9 +83,9 @@ export const AccountsPage = () => (
81
83
  );
82
84
  ```
83
85
 
84
- Only `DataPage.Columns` and `DataPage.MenuItems` exist as compound members.
85
- `MenuItem` and `Column` are **named exports** — there is no `DataPage.MenuItem`
86
- and no `DataPage.Column`. Pass `dataKey` whenever the read model has an
86
+ `DataPage.Columns`, `DataPage.MenuItems` and `DataPage.MenuItem` are the
87
+ compound members; `MenuItem` and `Column` are also **named exports** of the
88
+ subpath (there is no `DataPage.Column`). Pass `dataKey` whenever the read model has an
87
89
  identity.
88
90
 
89
91
  See [data-page.md](references/data-page.md) for every prop, and
@@ -97,6 +99,7 @@ Menu items go in `<DataPage.MenuItems>`. `MenuItem` takes `command`, not
97
99
  string. `disableOnUnselected` greys the item out until a row is selected.
98
100
 
99
101
  ```tsx
102
+ import { FaPencil, FaPlus } from 'react-icons/fa6';
100
103
  import { DataPage, MenuItem } from '@cratis/components/DataPage';
101
104
  import { useDialog } from '@cratis/arc.react/dialogs';
102
105
  import { CreateAccountDialog } from './CreateAccountDialog';
@@ -105,8 +108,8 @@ const [CreateAccountWrapper, showCreateAccount] = useDialog(CreateAccountDialog)
105
108
 
106
109
  <DataPage title='Accounts' query={AllAccounts} emptyMessage='No accounts yet.'>
107
110
  <DataPage.MenuItems>
108
- <MenuItem label='Add account' icon={() => <i className='pi pi-plus' />} command={() => showCreateAccount()} />
109
- <MenuItem label='Edit account' icon={() => <i className='pi pi-pencil' />} command={() => showEditAccount()} disableOnUnselected />
111
+ <MenuItem label='Add account' icon={FaPlus} command={() => showCreateAccount()} />
112
+ <MenuItem label='Edit account' icon={FaPencil} command={() => showEditAccount()} disableOnUnselected />
110
113
  </DataPage.MenuItems>
111
114
  <DataPage.Columns>
112
115
  <Column field='name' header='Name' />
@@ -146,9 +149,9 @@ export const CreateAccountDialog = () => {
146
149
  };
147
150
  ```
148
151
 
149
- Never put a raw PrimeReact control inside a command dialog for a command value —
150
- it bypasses the field wrapper, so validation never re-runs and the submit button
151
- stays disabled. Seed values that must be present for validity with
152
+ Never put a raw control (a `Common` `TextInput`, a native `<input>`) inside a
153
+ command dialog for a command value — it is not bound to the command, so
154
+ validation never re-runs and the submit button stays disabled. Seed values that must be present for validity with
152
155
  `initialValues`, not `onBeforeExecute`.
153
156
 
154
157
  ## Step 5 — Confirming, and showing that work is in progress
@@ -5,19 +5,21 @@ optional details pane in one component. Import from
5
5
  `@cratis/components/DataPage`.
6
6
 
7
7
  ```tsx
8
+ import { FaPlus } from 'react-icons/fa6';
8
9
  import { Column, DataPage, MenuItem } from '@cratis/components/DataPage';
9
10
  import type { IDetailsComponentProps } from '@cratis/components/DataPage';
10
11
  ```
11
12
 
12
13
  ## Compound members
13
14
 
14
- Exactly two static members exist:
15
+ Three static members exist:
15
16
 
16
17
  - `DataPage.Columns` — wraps the `Column` elements.
17
18
  - `DataPage.MenuItems` — wraps the `MenuItem` elements.
19
+ - `DataPage.MenuItem` — the same component as the named `MenuItem` export.
18
20
 
19
21
  `Column` and `MenuItem` are named exports of the same subpath. There is no
20
- `DataPage.Column` and no `DataPage.MenuItem`.
22
+ `DataPage.Column`.
21
23
 
22
24
  ## `DataPageProps`
23
25
 
@@ -37,9 +39,10 @@ Exactly two static members exist:
37
39
  | `onRefresh` | `() => void` | invoked to re-fetch a snapshot query |
38
40
  | `actionsAriaLabel` | `string` | accessible name of the actions menubar, default `'Actions'` |
39
41
  | `tableClassName` | `string` | class on the inner table |
40
- | `tablePt` / `tablePtOptions` / `tableUnstyled` | PrimeReact pass-through | target the inner data table |
42
+ | `tablePt` | `DataTableParts` | per-part attributes on the inner data table (`tablePtOptions`/`tableUnstyled` are accepted no-ops) |
41
43
  | `menubarClassName` | `string` | class on the action menubar |
42
- | `menubarPt` / `menubarPtOptions` / `menubarUnstyled` | PrimeReact pass-through | target the action buttons |
44
+ | `menubarPt` | `ButtonParts` | per-part attributes on the action buttons (`menubarPtOptions`/`menubarUnstyled` are accepted no-ops) |
45
+ | `paginatorPt` | `TablePaginatorParts` | per-part attributes on the paginator |
43
46
  | `clientFiltering` | `boolean` | **deprecated and a no-op** — do not use in new code |
44
47
 
45
48
  ## Snapshot versus observable
@@ -64,7 +67,7 @@ after a command succeeds.
64
67
  reads. Icons are usually a small inline component:
65
68
 
66
69
  ```tsx
67
- <MenuItem label='Add account' icon={() => <i className='pi pi-plus' />} command={() => showCreate()} />
70
+ <MenuItem label='Add account' icon={FaPlus} command={() => showCreate()} />
68
71
  ```
69
72
 
70
73
  ## `IDetailsComponentProps<TDataType>`
@@ -99,7 +102,8 @@ declaration the table reads.
99
102
  | `showFilterMatchModes` | `boolean` |
100
103
  | `filterElement` | `(options: ColumnFilterElementOptions) => ReactNode` |
101
104
  | `filterLabels` | `Partial<ColumnFilterMenuLabels>` |
102
- | `selectionMode` | `'single' \| 'multiple'` |
105
+ | `filterPt` | `ColumnFilterMenuParts` — parts of the filter popup |
106
+ | `selectionMode` | `'single'` — the only implemented value; there is no checkbox multi-selection |
103
107
  | `style` / `className` | `React.CSSProperties` / `string` |
104
108
  | `headerStyle` / `headerClassName` | `React.CSSProperties` / `string` |
105
109
  | `bodyStyle` / `bodyClassName` | `React.CSSProperties` / `string` |
@@ -112,11 +116,11 @@ Use `body` for a computed or formatted cell:
112
116
 
113
117
  ## Type name caution
114
118
 
115
- `@cratis/components/DataPage` exports two different things called
116
- `ColumnProps` — the props of the `DataPage.Columns` wrapper and the re-exported
117
- column declaration props. Import `ColumnProps` from
118
- `@cratis/components/DataTables` when you need the column type, so the reference
119
- is unambiguous.
119
+ `ColumnProps` from `@cratis/components/DataPage` is the **column declaration**
120
+ type re-exported from `DataTables` (the explicit named re-export shadows the
121
+ `DataPage.Columns` wrapper's own `ColumnProps`, which is not reachable from the
122
+ subpath). Import it from `@cratis/components/DataTables` when you want the
123
+ reference to say so.
120
124
 
121
125
  ## Layout
122
126
 
@@ -40,7 +40,8 @@ Both components take the same prop names:
40
40
  | `globalFilterFields` | `string[] \| undefined` | enables the search box |
41
41
  | `defaultFilters` | `DataTableFilterMeta` | initial filter state |
42
42
  | `className` | `string` | |
43
- | `pt` / `ptOptions` / `unstyled` | PrimeReact pass-through | |
43
+ | `pt` | `DataTableParts` | per-part attributes (`ptOptions`/`unstyled` are accepted no-ops) |
44
+ | `paginatorPt` | `TablePaginatorParts` | per-part attributes on the paginator |
44
45
  | `paginatorClassName` | `string` | |
45
46
  | `paginatorAriaLabels` | paginator label overrides | localize the paginator |
46
47
  | `clientFiltering` | `boolean` | **deprecated and a no-op** |
@@ -58,7 +59,13 @@ Both components take the same prop names:
58
59
  </DataTableForQuery>
59
60
  ```
60
61
 
61
- ## Paging
62
+ ## Paging, sorting and filtering scope
63
+
64
+ Column sorting and filtering — and the search box — apply to the **currently
65
+ loaded page**, not the complete result. Complete-result filtering and sorting
66
+ belong in the query's arguments and in the server-side query, before paging;
67
+ `clientFiltering` does not change this and is a deprecated no-op. Server totals
68
+ remain authoritative for the paginator.
62
69
 
63
70
  Both tables page server-side through the Arc paging hooks with a fixed page
64
71
  size of 20 rows starting at page 0. The paginator renders only when the result
@@ -1,7 +1,7 @@
1
1
  # Dialogs reference
2
2
 
3
3
  The Cratis dialog wrappers own command execution, validation timing, busy
4
- state, and footer buttons. Never import `Dialog` from `primereact/dialog`.
4
+ state, and footer buttons. Never use a vendor or hand-rolled modal — dialogs are Components-owned.
5
5
 
6
6
  The **components** live in `@cratis/components`; the **hooks, enums, and
7
7
  context** live in `@cratis/arc.react/dialogs`. `@cratis/components` exports no
@@ -41,10 +41,11 @@ first two is the normal case. `showDialog(input?)` resolves to
41
41
  `[DialogResult, TResponse?]` when the dialog closes.
42
42
 
43
43
  Inside the dialog, prefer plain typed props for input and take `closeDialog`
44
- from `useDialogContext<TResponse>()`:
44
+ from `useDialogContext<TRequest, TResponse>()` — the **first** type parameter is
45
+ the request the dialog was shown with, the second the response it closes with:
45
46
 
46
47
  ```tsx
47
- const { request, closeDialog } = useDialogContext<AccountCreated>();
48
+ const { request, closeDialog } = useDialogContext<object, AccountCreated>();
48
49
  closeDialog(DialogResult.Ok, response);
49
50
  ```
50
51
 
@@ -86,7 +87,7 @@ The most-used props:
86
87
  | `validateOnInit` | validate on mount so pre-filled invalid values show immediately |
87
88
  | `initialFocus` | which control receives focus when the dialog opens |
88
89
  | `dismissable` | X, Escape, and backdrop dismissal |
89
- | `pt` / `ptOptions` / `unstyled` | PrimeReact pass-through |
90
+ | `pt` | `DialogParts` — per-part attributes (`backdrop`, `positioner`, `root`, `header`, `title`, `close`, `content`, `footer`, `confirm`, `cancel`); `ptOptions`/`unstyled` are accepted no-ops |
90
91
 
91
92
  `CommandDialog.Column` exists for multi-column layouts:
92
93
 
@@ -132,8 +133,8 @@ wrapper close. Do not use `onConfirm` as a result handler.
132
133
 
133
134
  Every user-entered command value must be bound through a `CommandForm` field
134
135
  from `@cratis/components/CommandForm` (also published as
135
- `@cratis/components/CommandForm/fields`, the same module). A raw PrimeReact
136
- control bypasses the field wrapper, so validation never re-runs and the submit
136
+ `@cratis/components/CommandForm/fields`, the same module). A raw control (a
137
+ `Common` `TextInput`, a native `<input>`) is not bound to the command, so validation never re-runs and the submit
137
138
  button stays disabled.
138
139
 
139
140
  The full catalog: `InputTextField`, `PasswordField`, `NumberField`,
@@ -173,7 +174,7 @@ property descriptors, with `exclude` for the ones you do not want:
173
174
 
174
175
  ```tsx
175
176
  export const RenameDialog = () => {
176
- const { closeDialog } = useDialogContext<{ name: string }>();
177
+ const { closeDialog } = useDialogContext<object, { name: string }>();
177
178
  const [name, setName] = useState('');
178
179
 
179
180
  return (
@@ -197,9 +198,10 @@ when omitted, `isBusy` `false`, `okLabel` `'Ok'`, `cancelLabel` `'Cancel'`,
197
198
  Two behaviors worth knowing:
198
199
 
199
200
  - `resizable` is accepted for compatibility but has **no effect** — the
200
- PrimeReact 11 headless dialog has no resize.
201
+ Components dialog has no resize.
201
202
  - Passing a custom node to `buttons` instead of a `DialogButtons` member
202
- removes the header close control, disables Escape, and means `onClose`,
203
+ removes the header close control and disables Escape/backdrop dismissal
204
+ (unless you also pass `dismissable` explicitly), and means `onClose`,
203
205
  `onCancel`, and `onConfirm` are never invoked. Close through
204
206
  `useDialogContext().closeDialog(...)`, or set `dismissable` explicitly.
205
207
 
@@ -48,6 +48,30 @@ Guard rendering with `hasData` rather than checking `data.length` — a
48
48
  conditional query that has not fired returns an empty result with
49
49
  `hasData: false`.
50
50
 
51
+ ### Command success is not projection convergence
52
+
53
+ A successful `CommandResult` means the events were **appended**. The read model a
54
+ query reads is built by an observer that runs *after* that, so for a moment the
55
+ command has succeeded and the query still answers from before it. Model the
56
+ states separately instead of collapsing them into "loaded or not":
57
+
58
+ | State | Signal | Render |
59
+ | --- | --- | --- |
60
+ | Not asked yet | `.when(false)` / `!isReady` | nothing, or the previous content |
61
+ | Pending | `isPerforming`, or `hasData: false` **right after a command you just issued succeeded** | a skeleton — **never** "not found" |
62
+ | Converged, empty | `hasData` with an empty `data` and no recent command | the designed empty state |
63
+ | Converged, populated | `hasData` with items | the content |
64
+ | Failed | `!isSuccess` / `hasExceptions` / `!isAuthorized` | the matching error affordance |
65
+ | Stale refresh | `isPerforming` while `data` still holds the previous result | keep the old content, indicate refresh |
66
+
67
+ Two consequences. After a create command, take the **window action on the
68
+ command's success** (close the dialog, navigate to the new id) and let the list
69
+ catch up — do not wait for the query to show the row before closing. And for a
70
+ detail page opened on an id you just created, `hasData: false` is *pending*, not
71
+ *missing*: an observable query (`ISubject<T>` on the backend, `.use()` here)
72
+ resolves it the moment the projection lands, which is why observable reads are
73
+ the default for anything a user changes and then looks at.
74
+
51
75
  ### Paging
52
76
 
53
77
  ```ts
@@ -196,5 +220,5 @@ query transport. Its props include `microservice`, `origin`, `basePath`,
196
220
  `apiBasePath`, `httpHeadersCallback` (merged into every request — bearer and
197
221
  tenant headers), `detailsType`, `queryTransportMethod`, `queryConnectionCount`,
198
222
  `queryDirectMode`, `observableQueryTransferMode`, and `queryCacheRetentionMs`.
199
- `QueryTransportMethod` and `ObservableQueryTransferMode` import from
200
- `@cratis/arc`; `Arc` imports from `@cratis/arc.react`.
223
+ `QueryTransportMethod` imports from `@cratis/arc/queries`,
224
+ `ObservableQueryTransferMode` from `@cratis/arc`; `Arc` imports from `@cratis/arc.react`.