@cratis/pi 2.21.3 → 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
@@ -24,8 +24,8 @@ This skill is verified against these exact sources:
24
24
 
25
25
  | Package | Version | Purpose |
26
26
  | --- | --- | --- |
27
- | `Cratis.Chronicle` | `16.45.2` | `Cratis.Chronicle.Reactors`, `Cratis.Chronicle.Reactors.SideEffects`, `Cratis.Chronicle.EventSequences`, `Cratis.Chronicle.Events` |
28
- | `Cratis.Chronicle.CodeAnalysis` | `16.45.2` | `CHR0004`, `CHR0005`, `CHR0008`, `CHR0013`, `CHR0022`, `CHR0031`, `CHR0032` |
27
+ | `Cratis.Chronicle` | `18.3.0` | `Cratis.Chronicle.Reactors`, `Cratis.Chronicle.Reactors.SideEffects`, `Cratis.Chronicle.EventSequences`, `Cratis.Chronicle.Events` |
28
+ | `Cratis.Chronicle.CodeAnalysis` | `18.3.0` | `CHR0004`, `CHR0005`, `CHR0008`, `CHR0013`, `CHR0022`, `CHR0031`, `CHR0032` |
29
29
 
30
30
  Reverify product sources before claiming support for another version.
31
31
 
@@ -280,19 +280,33 @@ selection, a reactor or a reducer is where it happens.
280
280
  ## Translation via a command
281
281
 
282
282
  A translation that adapts one area's events into another's intent runs a command
283
- rather than appending directly. `ICommandPipeline` is an **Arc** type
284
- (`Cratis.Arc.Commands`), not a Chronicle one:
283
+ rather than appending directly. With the Arc Chronicle integration the handler
284
+ **returns** the command — the same shape as returning an event — and Arc executes
285
+ it through validation and authorization; a denied, invalid or throwing result is a
286
+ side-effect failure that fails the partition rather than being dropped:
285
287
 
286
288
  ```csharp
287
- using Cratis.Arc.Commands;
289
+ using Cratis.Arc.Chronicle.Reactors;
288
290
 
289
- public class <ReactorName>(ICommandPipeline commandPipeline) : IReactor
291
+ [ExecuteCommandsAsSystem("<role the command requires>")] // only when the command is [Roles]/[Authorize]-gated
292
+ public class <ReactorName> : IReactor
290
293
  {
291
- public async Task <MethodName>(<EventName> @event, EventContext context) =>
292
- await commandPipeline.Execute(new <CommandName>(@event.<Property>));
294
+ [OnceOnly]
295
+ public Task<<CommandName>> <MethodName>(<EventName> @event, EventContext context) =>
296
+ Task.FromResult(new <CommandName>(@event.<Property>));
293
297
  }
294
298
  ```
295
299
 
300
+ A reactor runs with **no principal**; `[ExecuteCommandsAsSystem]` (class-level,
301
+ `Cratis.Arc.Chronicle.Reactors`) supplies one for the **returned** commands only.
302
+ A collection return is executed as commands when every element is a command.
303
+
304
+ The imperative form — inject `ICommandPipeline` (`Cratis.Arc.Commands`) and call
305
+ `Execute(command)` — is still supported and is the subject of the
306
+ **cratis-arc-command-execution** skill. It needs `[OnceOnly]` as well (`ARCCHR0006`
307
+ warns), the `CommandResult` must be inspected, and `[ExecuteCommandsAsSystem]`
308
+ does not cover it.
309
+
296
310
  ## Failure behavior
297
311
 
298
312
  If a handler throws, or a returned side-effect event fails to append — a
@@ -23,7 +23,7 @@ This skill is verified against these exact sources:
23
23
 
24
24
  | Package | Version | Purpose |
25
25
  | --- | --- | --- |
26
- | `Cratis.Chronicle` | `16.45.2` | `Cratis.Chronicle.Events`, `Cratis.Chronicle.Keys`, `Cratis.Chronicle.ReadModels` |
26
+ | `Cratis.Chronicle` | `18.3.0` | `Cratis.Chronicle.Events`, `Cratis.Chronicle.Keys`, `Cratis.Chronicle.ReadModels` |
27
27
  | `Cratis.Arc.Core` | current | `Cratis.Arc.Queries.ModelBound.ReadModelAttribute` and `PathAttribute` |
28
28
  | `Cratis.Arc.MongoDB` | current | the `Observe` family of `IMongoCollection<T>` extensions |
29
29
 
@@ -1,7 +1,7 @@
1
1
  # Read-model queries
2
2
 
3
3
  Verified against `Cratis.Arc.Core` and `Cratis.Arc.MongoDB` as checked out
4
- alongside `Cratis.Chronicle` `16.45.2`. Confirm the exact Arc package version in
4
+ alongside `Cratis.Chronicle` `18.3.0`. Confirm the exact Arc package version in
5
5
  the consuming project before citing one.
6
6
 
7
7
  ## Where the pieces live
@@ -17,9 +17,11 @@ separate `When`: seeding the events is what makes the projection run.
17
17
 
18
18
  | Package | Version | Purpose |
19
19
  | --- | --- | --- |
20
- | `Cratis.Chronicle.Testing` | `16.45.2` | `ReadModelScenario<TReadModel>` and its `Given` builder |
20
+ | `Cratis.Chronicle.Testing` | `18.3.0` | `ReadModelScenario<TReadModel>` and its `Given` builder |
21
21
  | `Cratis.Specifications.XUnit` | `4.x` | The `Specification` base and the `ShouldXxx` assertions |
22
22
 
23
+ > 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.
24
+
23
25
  ```bash
24
26
  dotnet add package Cratis.Chronicle.Testing
25
27
  ```
@@ -17,7 +17,7 @@ This skill is verified against this exact source:
17
17
 
18
18
  | Package | Version | Purpose |
19
19
  | --- | --- | --- |
20
- | `Cratis.Chronicle` | `16.45.2` | `Cratis.Chronicle.Reducers`, `Cratis.Chronicle.ReadModels`, `Cratis.Chronicle.Events` |
20
+ | `Cratis.Chronicle` | `18.3.0` | `Cratis.Chronicle.Reducers`, `Cratis.Chronicle.ReadModels`, `Cratis.Chronicle.Events` |
21
21
 
22
22
  Reverify product sources before claiming support for another version.
23
23
 
@@ -22,7 +22,7 @@ This skill is verified against this exact source:
22
22
 
23
23
  | Package | Version | Purpose |
24
24
  | --- | --- | --- |
25
- | `Cratis.Chronicle` | `16.45.2` | the Workbench application, its hosting, screens, and commands |
25
+ | `Cratis.Chronicle` | `18.3.0` | the Workbench application, its hosting, screens, and commands |
26
26
 
27
27
  Reverify before claiming a screen, route, command, or configuration key for
28
28
  another version.
@@ -72,7 +72,9 @@ Read these before putting a Workbench on any reachable network.
72
72
  - The server is designed to sit behind a proxy and trusts the immediate proxy's
73
73
  forwarded headers unconditionally. That is only safe when the proxy is the
74
74
  only route in.
75
- - The health endpoint and the connection-count endpoints answer anonymously.
75
+ - The health endpoint, the antiforgery probe, the gRPC descriptor-set and
76
+ compatibility calls, the initial-admin-password endpoints, and the gRPC
77
+ `ResetKernelState` call answer anonymously (`[AllowAnonymous]`).
76
78
 
77
79
  ## The screens
78
80
 
@@ -80,16 +82,18 @@ Routes are grouped by scope. Event-store-scoped screens apply to the whole
80
82
  store; namespace-scoped screens apply to one tenant namespace.
81
83
 
82
84
  **Namespace-scoped:** Recommendations (the landing screen) · Jobs · Sequences ·
83
- Pivot (experimental) · Behavior patterns · Pattern heatmap · Observers · Failed
84
- partitions · Read Models · Identities · Seed Data.
85
+ Pivot · Patterns · Pattern heatmap · Observers · Failed partitions · Read Models ·
86
+ Identities · Seed Data.
85
87
 
86
88
  **Event-store-scoped:** Event Types · Read Model Types · Webhooks · External
87
- Services · Captures · Projections · Namespaces · Seed Data.
89
+ Services · Captures · Projections (namespace-routed) · Namespaces · Seed Data.
88
90
 
89
- **System:** Users · Applications · Connected Clients · Development Tools.
91
+ **System:** Users · Applications · Connected Clients · Servers · Development
92
+ Tools (offered only when the server reports them available).
90
93
 
91
- Reducers, Reactors, and Sinks have routes but no menu entry. A Dashboard exists
92
- in the source but is fully disabled — it is not a shipping screen.
94
+ Reducers, Reactors, Sinks and a store-wide Sequences view have routes but no menu
95
+ entry (their menu lines are commented out in `EventStore.tsx`). There is no
96
+ Dashboard screen; only a `Card`/`Chart` component pair written for one remains.
93
97
 
94
98
  ## What each screen can change
95
99
 
@@ -125,9 +129,13 @@ partition, stop/resume/delete job, and perform recommendation all fire on the
125
129
  first click. Decide before clicking, not after.
126
130
 
127
131
  **Reset kernel state is the most destructive operation in the product.** It is
128
- gated three ways — the endpoint exists only in development builds, the screen
129
- reports itself unavailable when the server says so, and a confirmation dialog
130
- guards the button. Never run it against a store whose data anyone still needs.
132
+ gated three ways — the *operation* is compiled in only for development builds
133
+ (`KernelStateResetter.IsAvailable`; in any other build the gRPC call still exists,
134
+ answers anonymously, and is a no-op), the screen reports itself unavailable when
135
+ the server says so, and a confirmation dialog guards the button. Never run it
136
+ against a store whose data anyone still needs, and never assume a production
137
+ build is protected by authentication here — it is protected by the operation
138
+ not being there.
131
139
 
132
140
  ## Redaction and revision
133
141
 
@@ -24,7 +24,9 @@ This skill is verified against this exact source:
24
24
 
25
25
  | Package | Version | Purpose |
26
26
  | --- | --- | --- |
27
- | `Cratis.Cli` | `2.4.0` | `cratis chronicle workbench`, its views, keys, and actions |
27
+ | `Cratis.Cli` | `3.2.0` | `cratis chronicle workbench`, its views, keys, and actions |
28
+
29
+ > Re-verified at `Cratis/cli` **v3.2.0**: the fifteen navigation views and every key binding below exist in `Source/Cli/Commands/Chronicle/Workbench` at that tag.
28
30
 
29
31
  Reverify before claiming a view, key binding, or action for another version.
30
32
  `cratis chronicle workbench --help` and `cratis llm-context` are the authority
@@ -61,9 +63,11 @@ The navigation pane groups fifteen views into five sections:
61
63
  | Projections | Projections · Read Models |
62
64
  | Server | Event Stores · Namespaces · Applications · Users · Identities · Subscriptions |
63
65
 
64
- Six further detail views — observer, failed partition, event, event type,
65
- projection, and read model — open from a row and never appear in the navigation
66
- pane.
66
+ Every table view renders a detail for its selected row — `RenderDetail` is
67
+ abstract on the shared `FilterableTableView`, so activating a row always
68
+ surfaces its full detail in the detail pane or a centered overlay; the event
69
+ and read-model views open richer overlays of their own. Detail views never
70
+ appear in the navigation pane.
67
71
 
68
72
  Each refresh fetches one consistent snapshot of the store: version info, event
69
73
  stores, namespaces, observers, failed partitions, jobs, recommendations, event
@@ -127,8 +131,9 @@ Several views bind a key to an operation that changes the running server:
127
131
  Each has a bulk form that applies to every checked row.
128
132
 
129
133
  Every one of these opens a centered confirmation modal that states the action
130
- cannot be undone, confirmed with `Enter` or `Y` and cancelled with `Escape` or
131
- `N`. **That modal is not authorization.** A request to inspect a live store does
134
+ cannot be undone. **Only an explicit `Y` (or the Confirm button) confirms;
135
+ `Enter`, `Escape` and `N` all cancel** — the modal is cancel-focused on purpose,
136
+ so a reflexive Enter never runs a destructive action. **That modal is not authorization.** A request to inspect a live store does
132
137
  not authorize replay, retry, stop, resume, perform, or ignore. Before pressing
133
138
  one of those keys:
134
139
 
@@ -18,9 +18,11 @@ change did not touch is a note, never a blocker.
18
18
 
19
19
  | Package | Version | Purpose |
20
20
  | --- | --- | --- |
21
- | `Cratis.Arc.Core` | `22.10.4` | Command, query, validation and analyzer surface (`ARC0001`–`ARC0015`) |
22
- | `Cratis.Chronicle` | `16.45.2` | Event, projection, read-model and constraint surface |
23
- | `Cratis.Fundamentals` | `7.18.2` | `ConceptAs<T>`, `IInstancesOf<T>`, the DI conventions |
21
+ | `Cratis.Arc.Core` | `22.16.0` | Command, query, validation and analyzer surface (`ARC0001`–`ARC0015` in `DiagnosticDescriptors`, plus `ARC0016`–`ARC0018` from the command-operation analyzer) |
22
+ | `Cratis.Chronicle` | `18.3.0` | Event, projection, read-model and constraint surface |
23
+ | `Cratis.Fundamentals` | `7.19.2` | `ConceptAs<T>`, `IInstancesOf<T>`, the DI conventions |
24
+
25
+ > 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.
24
26
 
25
27
  Reverify against the owning product repository before asserting a framework
26
28
  contract this file does not already state.
@@ -97,9 +99,9 @@ there is nothing to review under a red build.
97
99
  - No `@ts-ignore` or `@ts-expect-error` without a comment saying why.
98
100
  - Full descriptive names — never `e`, `idx`, `prev`, `dir`, `pos`.
99
101
  - `CommandDialog` from `@cratis/components/CommandDialog` for command dialogs;
100
- `Dialog` from `@cratis/components/Dialogs` for data-only dialogs. **Never**
101
- `Dialog` from `primereact/dialog`.
102
- - No hard-coded hex or rgb colors — PrimeReact CSS variables only. No
102
+ `Dialog` from `@cratis/components/Dialogs` for data-only dialogs. **Never** a
103
+ vendor or hand-rolled modal.
104
+ - No hard-coded hex or rgb colors — `--cratis-*` tokens only. No
103
105
  `!important` without a justifying comment.
104
106
  - Components live in the slice folder. No barrel `index.ts` that re-exports one
105
107
  component, and no technical `hooks/` / `utils/` / `types/` grouping at feature
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: cratis-components-accessibility
3
- description: Apply the accessibility conventions Cratis Components actually implements — dialog initial-focus discipline and the armed-Enter hazard, supplying every accessible name as a localizable prop, putting the accessible name on the focusable element rather than the wrapper, and using the PrimeReact pass-through prop to strip invalid ARIA. Use when building or reviewing a dialog, a form control, a data table, a toast, or any component whose accessible name is currently a hard-coded English string. Do not use as a general WCAG conformance guide.
3
+ description: Apply the accessibility conventions Cratis Components 4 actually implements — dialog initial-focus discipline and the armed-Enter hazard, supplying every accessible name as a localizable prop or provider message, putting the accessible name on the focusable element rather than the wrapper, keeping decorative icons hidden, and customizing through parts without fighting the component's own ARIA. Use when building or reviewing a dialog, a form control, a data table, a toast, or any component whose accessible name is currently a hard-coded English string. Do not use as a general WCAG conformance guide.
4
4
  license: MIT
5
5
  ---
6
6
 
@@ -17,8 +17,11 @@ what is written here.
17
17
 
18
18
  | Package | Version | Verified from |
19
19
  | --- | --- | --- |
20
- | `@cratis/components` | `3.0.0` | its component sources and specifications |
21
- | `primereact` | `^11.0.0` | peer of `@cratis/components@3.0.0` |
20
+ | `@cratis/components` | `4.6.0` | `Source/Dialogs/*` (`DialogInitialFocus`, `DialogImplementation`), `Source/CommandForm/fields/fieldAccessibility.tsx`, `Source/for_accessibility/*`, `Source/SchemaEditor/*`, `Source/ObjectNavigationalBar/*`, `Source/MIGRATION.md`, `Documentation/Styling/pass-through.md`, `Documentation/Common/cratis-components-provider.md`, `Documentation/renderers/index.md` |
21
+
22
+ Focus, overlay and collection behavior come from React Aria **internally** — the
23
+ dialog is a React Aria `Modal` inside a `ModalOverlay`; you never import React
24
+ Aria yourself, and its class names are not a styling or testing hook.
22
25
 
23
26
  ## 1. Dialog focus discipline
24
27
 
@@ -58,119 +61,146 @@ read anything.
58
61
  `Cancel` degrades to `Content` automatically when the chosen button set has
59
62
  nothing to dismiss with, so it is always safe to ask for.
60
63
 
61
- `DialogInitialFocus` imports from `@cratis/components/Dialogs`.
64
+ `DialogInitialFocus` imports from `@cratis/components/Dialogs`. While `isBusy`
65
+ is set the dialog marks its content `inert` and disables every dismissal path,
66
+ so a user cannot interact with a half-committed form.
62
67
 
63
- ## 2. Every accessible name is a prop — and never a baked-in English string
68
+ ## 2. Every accessible name is a prop or a provider message — never a baked-in English string
64
69
 
65
70
  This is the library's strongest and most consistent convention: any string a
66
- screen reader will read is exposed as an overridable, localizable prop. Set it.
67
-
68
- | Component | Prop |
69
- | --- | --- |
70
- | `Dialog` (and `CommandDialog`) | `closeAriaLabel` — default `'Close'` |
71
- | `DataPage` | `actionsAriaLabel` — default `'Actions'` |
72
- | `DataTableForQuery` / `DataTableForObservableQuery` | `paginatorAriaLabels` |
73
- | `Column` (filter menu) | `filterLabels` — trigger name, clear, apply, true, false |
74
- | `Toaster` | `dismissAriaLabel` — default `'Dismiss'` |
75
- | `Dropdown` | `aria-label`, `aria-labelledby`, `aria-describedby` |
76
- | `SchemaEditor` | `labels` — ten strings, five of them accessible names |
77
- | `ObjectNavigationalBar` | `backLabel` — tooltip **and** accessible name |
78
- | `ToolbarButton` | `title` — required; the accessible name and the tooltip |
79
- | `ToolbarFanOutItem` | `tooltip` — required |
80
- | `RatingField` | `starAriaLabel` |
81
- | `ChipsField` | `removeAriaLabel` |
82
-
83
- Where the library can resolve a name from a locale it does — the table
84
- paginator reads its navigation labels from the PrimeReact locale rather than
85
- hard-coding them, and the busy-indicator dialog names its progress indicator
86
- from the consumer-supplied, already-localized message. Follow that pattern:
87
- **never bake an English accessible name into application code.**
71
+ screen reader will read is exposed as an overridable, localizable prop, and the
72
+ library-wide ones also come from `CratisComponentsProvider`'s `messages`. The
73
+ precedence is always the same: **a named component prop wins, then the matching
74
+ provider message, then the English default.** Set the provider messages once for
75
+ the application's locale, and the per-instance props where one control needs a
76
+ different name.
77
+
78
+ | Component | Prop | Provider message |
79
+ | --- | --- | --- |
80
+ | `Dialog` / `CommandDialog` / `StepperCommandDialog` | `closeAriaLabel`; `okLabel`, `cancelLabel`, `yesLabel`, `noLabel` | `messages.dialog` (`ok`, `cancel`, `yes`, `no`, `close`) |
81
+ | `CommandStepper` / `StepperCommandDialog` | `nextLabel`, `previousLabel` | `messages.stepper` (`next`, `previous`, `submit`) |
82
+ | `DataPage` | `actionsAriaLabel`, `globalSearchAriaLabel`, `paginatorAriaLabels` | `messages.dataTable`, `messages.paginator` |
83
+ | `DataTableForQuery` / `DataTableForObservableQuery` | `paginatorAriaLabels` | `messages.paginator` (`navigation`, `first`, `previous`, `next`, `last`) |
84
+ | `Column` (filter menu) | `filterLabels` — match mode, clear, apply, true, false | `messages.columnFilter` |
85
+ | `Toaster` | `dismissAriaLabel` | `messages.notifications` (`dismiss`, `region`) |
86
+ | `Dropdown` | `aria-label`, `aria-labelledby`, `aria-describedby` | `messages.dropdown` (`showOptions`, `clearSelection`) |
87
+ | `DatePickerInput` / `CalendarField` | `aria-label` / `aria-labelledby` for the segmented **group**; `todayLabel`, `clearLabel` | `messages.datePicker` (`today`, `clear`, `openCalendar`, `previousMonth`, `nextMonth`, `label`) |
88
+ | Every CommandForm field | `aria-label` (falls back to the field `title`), `aria-describedby`, `id` | — |
89
+ | `SchemaEditor` | `labels` | — |
90
+ | `ObjectNavigationalBar` | `backLabel` — tooltip **and** accessible name | — |
91
+ | `ToolbarButton` | `title` — required; the accessible name and the tooltip | — |
92
+ | `Toolbar` | `aria-label` | `messages.toolbar` (`label`, default `'Tools'`) |
93
+ | `ToolbarFanOutItem` / `ToolbarFolder` | `tooltip` / `title` — required | — |
94
+ | `RatingField` | `starAriaLabel` | — |
95
+ | `ChipsField` | `removeAriaLabel` | — |
88
96
 
89
97
  In a localized application, treat any component whose accessible name you have
90
- not supplied as a defect.
98
+ not supplied — and any provider message group left at its English default — as
99
+ a defect.
91
100
 
92
101
  ## 3. Put the name on the focusable element, not the wrapper
93
102
 
94
103
  An accessible name has to land on the element that actually receives focus and
95
- carries the role. `Dropdown` demonstrates the rule: `id`, `tabIndex`, and all
96
- three ARIA attributes route to the combobox trigger, not to the layout wrapper,
97
- and the control id is deliberately not duplicated onto the wrapper so an
98
- external `<label htmlFor>` associates correctly.
104
+ carries the role. `Dropdown` demonstrates the rule: `id` and the `aria-*`
105
+ attributes route to the **trigger** (`data-cratis-part='trigger'`), not to the
106
+ layout wrapper, and the control id is deliberately not duplicated onto the
107
+ wrapper so an external `<label htmlFor>` associates correctly. A single select
108
+ follows the button/listbox pattern and a filtered one is a combobox — so in a
109
+ test, query the trigger by its accessible name or its part, **not** by
110
+ `role="combobox"`.
99
111
 
100
112
  ```tsx
101
113
  <label htmlFor='role'>Advisory role</label>
102
114
  <Dropdown id='role' value={role} options={roles} aria-describedby='role-help' />
103
115
  ```
104
116
 
117
+ `DatePickerInput` is a segmented field: its `id` identifies the **focus group**,
118
+ not a native text input, so name it with `aria-label`/`aria-labelledby` on the
119
+ group. `Tooltip` enhances exactly one real focusable child so focus, hover and
120
+ `aria-describedby` stay together — wrap text, fragments or several siblings in
121
+ one native control first. Several `RadioButtonField`s bound to one property need
122
+ the same explicit `name` for native arrow-key group navigation.
123
+
105
124
  Apply the same rule to your own wrapper components: forward `id`, `tabIndex`,
106
125
  and `aria-*` down to the focusable child rather than putting them on a
107
126
  container.
108
127
 
109
- ## 4. Decorative icons are hidden
110
-
111
- Icons and loading spinners inside a labelled control are marked
112
- `aria-hidden='true'` so a button's accessible name is exactly its label and not
113
- "pi pi-plus Add account". Do the same in application components: an icon that
114
- sits beside a text label carries no information of its own.
128
+ ## 4. Field errors are announced — do not duplicate them
115
129
 
116
- ## 5. Use pass-through to strip invalid ARIA
130
+ Every CommandForm field runs its title and validation messages through one
131
+ helper: the control's accessible name falls back to the field `title`, and when
132
+ the field has errors a visually hidden `<span id="…-errors">` is rendered and
133
+ linked from the control's `aria-describedby` (merged with any `aria-describedby`
134
+ you pass). Arc renders the visible error text; the hidden span exists only for
135
+ assistive technology. Do **not** render a second announcement or wire your own
136
+ `aria-describedby` to the visible error — you would announce it twice.
117
137
 
118
- Setting a pass-through value to `undefined` **removes** that attribute. Cratis
119
- Components uses this deliberately where PrimeReact 11 emits ARIA that is invalid
120
- or points at ids it never renders — for example removing `aria-sort` from a sort
121
- control that carries `role="button"`, and removing `role` and `aria-controls`
122
- from stepper headers whose referenced panels have no matching ids.
138
+ ## 5. Decorative icons are hidden
123
139
 
124
- ```tsx
125
- pt={{ root: { role: undefined, 'aria-controls': undefined } }}
126
- ```
140
+ Icons and loading spinners inside a labelled control are marked
141
+ `aria-hidden='true'` so a button's accessible name is exactly its label and not
142
+ the icon's text. Do the same in application components: an icon that sits beside
143
+ a text label carries no information of its own (`<FaPlus aria-hidden='true' />`).
127
144
 
128
- Reach for this when an upstream primitive emits an attribute that is wrong for
129
- the element it lands on. Removing a broken role usually leaves a natively
130
- keyboard-accessible element behind, which is better than a role that lies.
145
+ ## 6. Customize through parts without fighting the component's ARIA
131
146
 
132
- ## 6. Prefer an invalid state over an invalid attribute
147
+ Parts (`pt` and `data-cratis-part`) are for **presentation**. The component owns
148
+ the role, the name and the state attributes on its elements: *named behavior
149
+ props override conflicting part attributes*, so a `role` or `aria-*` value you
150
+ push through `pt` onto a part the component already labels is either overridden
151
+ or produces two competing semantics. Put identity and ARIA on the documented
152
+ behavior props (§2, §3) and keep `pt` to `className`, `style` and data
153
+ attributes. State is expressed through valid ARIA plus a data attribute
154
+ (`aria-invalid` + `data-invalid`, `data-disabled`, `data-selected`) — express your
155
+ own component state the same way, never through made-up attributes.
133
156
 
134
- Where a control needs to express invalidity, the library translates it to
135
- `aria-invalid` plus a data attribute rather than forwarding an attribute the DOM
136
- would reject. Follow that: express state through valid ARIA, not through
137
- made-up attributes.
157
+ One focus lifecycle per interactive surface: never nest a second modal or focus
158
+ trap inside a Components dialog to borrow another look, and never combine two
159
+ keyboard-selection owners for one control. A presentation adapter must preserve
160
+ the Components contract without adding a second semantic control.
138
161
 
139
- ## What Cratis Components does **not** give you
162
+ ## What Cratis Components does — and does not — give you
140
163
 
141
164
  Be accurate about this — over-promising accessibility is worse than saying
142
165
  nothing.
143
166
 
144
- - **No accessibility linting or scanning is configured.** The library's ESLint
145
- configuration has no `jsx-a11y` plugin, and no axe or Storybook
146
- accessibility addon is installed. There is **no automated accessibility gate**
147
- in the quality gates. Any accessibility claim comes from a manual audit.
148
- - **The schema editors, the object content editor, and the object navigational
149
- bar are click-only.** Their breadcrumbs and drill-in affordances are plain
150
- elements with click handlers — no role, no `tabIndex`, no key handling. Do not
151
- document keyboard navigation for them.
152
- - **Field-level error text is not wired to its input.** A validation message
153
- shown beside a field is not associated through `aria-describedby` or
154
- `aria-errormessage`. If your application needs that association, add it in the
155
- application component.
156
- - **There is no focus trap, roving tabindex, skip link, or live region** beyond
157
- what the dialog and the PrimeReact primitives provide.
158
- - Some existing Cratis Components documentation describes keyboard shortcuts and
159
- ARIA support for the schema editor and the navigational bar that the code does
160
- not implement. Verify against the component source before repeating any such
161
- claim.
167
+ - **There is an automated scan, on representative surfaces.** The library runs
168
+ `axe-core` (WCAG 2 A and AA rules) over a set of representative foundation
169
+ surfaces in its own specs, and Storybook carries the accessibility addon. That
170
+ proves those surfaces have no *automated* violations; it is not a conformance
171
+ certification, it does not cover every component, and it cannot see keyboard
172
+ order, focus restoration, screen-reader phrasing or color relationships the
173
+ product introduces.
174
+ - **Modal focus management is real**: focus moves in per §1, the backdrop and the
175
+ document behind are handled by the React Aria modal, Escape and backdrop
176
+ dismissal follow `dismissable`, and busy content is `inert`. Verify keyboard
177
+ order, initial focus, focus restoration and screen-reader output in the
178
+ application's supported browsers and assistive technologies — Components does
179
+ not certify them for you.
180
+ - **Breadcrumbs and drill-ins in `SchemaEditor`, `ObjectContentEditor` and
181
+ `ObjectNavigationalBar` are native `<button>`s** (with `aria-current` on the
182
+ current crumb), so they are focusable and keyboard-activatable. Anything beyond
183
+ that — shortcut keys, arrow-key grid navigation — is not implemented; verify
184
+ against the component source before documenting it.
185
+ - **A custom design still owns visible focus, contrast, target size, reduced
186
+ motion, forced colors and responsive behavior.** Components supplies semantic
187
+ behavior and the `--cratis-focus-ring` token; it cannot infer the product's
188
+ color relationships.
189
+ - **No skip links, roving-tabindex utilities or live regions** for application
190
+ layouts beyond the toast region (`messages.notifications.region` names it).
162
191
 
163
192
  ## Verify
164
193
 
165
194
  - Every dialog whose confirm action is destructive or irreversible sets
166
195
  `initialFocus` to `DialogInitialFocus.Cancel` or `Content`.
167
- - No accessible name in application code is a hard-coded English string — each
168
- comes from the component's label prop, resolved from the application's
169
- locale.
196
+ - `CratisComponentsProvider` carries the locale and every `messages` group the
197
+ application renders; no accessible name in application code is a hard-coded
198
+ English string.
170
199
  - `id`, `tabIndex`, and `aria-*` on a wrapper component reach the focusable
171
- child, not the container.
200
+ child, not the container; a `DatePickerInput` is named on its group.
172
201
  - Icons beside a text label are `aria-hidden`.
173
- - Any ARIA attribute removed from an upstream primitive is removed through
174
- pass-through `undefined`, with a comment saying why.
175
- - No claim of keyboard support, automated accessibility scanning, or WCAG
176
- conformance is made that the source does not back.
202
+ - No `role`/`aria-*` is injected through `pt`; no CSS or test targets a React Aria
203
+ class name.
204
+ - Field errors are not announced twice.
205
+ - No claim of keyboard support, automated coverage, or WCAG conformance is made
206
+ that the source does not back.
@@ -21,9 +21,8 @@ case where the shape itself is data.
21
21
 
22
22
  | Package | Version | Verified from |
23
23
  | --- | --- | --- |
24
- | `@cratis/components` | `3.0.0` | its package manifest and the `SchemaEditor` / `ObjectContentEditor` sources |
25
- | `primereact` | `^11.0.0` | peer of `@cratis/components@3.0.0` |
26
- | `react` | `^19.0.0` | peer of `@cratis/components@3.0.0` |
24
+ | `@cratis/components` | `4.6.0` | its package manifest and the `SchemaEditor` / `ObjectContentEditor` sources |
25
+ | `react` | `^19.0.0` | peer of `@cratis/components@4.6.0` |
27
26
 
28
27
  ## The schema type these editors consume
29
28
 
@@ -147,10 +146,11 @@ among its siblings. Save is disabled while any name is invalid.
147
146
 
148
147
  ### Localization
149
148
 
150
- `SchemaEditorLabels` has ten fields, five of them accessible names:
151
- `edit`, `save`, `cancel`, `addProperty`, `actions`, `navigateBack`,
152
- `emptyMessage`, `navigateToItemDefinition`, `navigateToProperties`,
153
- `deleteProperty`. Pass `labels` with the ones you want to change — they merge
149
+ `SchemaEditorLabels` has fourteen fields, nine of them accessible names:
150
+ menu actions `edit`, `save`, `cancel`, `addProperty`; accessible names `actions`,
151
+ `navigateBack`, `navigateToItemDefinition`, `navigateToProperties`,
152
+ `propertyName`, `propertyType`, `arrayItemType`, `deleteProperty`; and the
153
+ messages `emptyMessage`, `invalidJson`. Pass `labels` with the ones you want to change — they merge
154
154
  over the English defaults. Never rely on the built-in English strings in a
155
155
  localized application.
156
156
 
@@ -218,9 +218,11 @@ name — localize it). It is click-only, like the editors' own breadcrumbs.
218
218
 
219
219
  ## Styling
220
220
 
221
- Both editors expose `className` only. Restyle their internals through a global
222
- PrimeReact pass-through preset on the provider rather than per instance — see
223
- the **cratis-components-styling** skill.
221
+ Both editors expose `className` only — no `pt`. Restyle their internals with CSS
222
+ scoped under that class, reaching the controls they render (buttons, inputs,
223
+ the embedded `Dropdown`) through their `data-cratis-part` values and the
224
+ `--cratis-*` tokens; there is no global provider pass-through in Components 4 —
225
+ see the **cratis-components-styling** skill.
224
226
 
225
227
  ## Verify
226
228
 
@@ -14,9 +14,8 @@ field across every step is valid.
14
14
 
15
15
  | Package | Version | Verified from |
16
16
  | --- | --- | --- |
17
- | `@cratis/components` | `3.0.0` | its package manifest and `CommandDialog` sources |
18
- | `@cratis/arc.react` | `>=20.3.1 <23` | peer range declared by `@cratis/components@3.0.0` |
19
- | `primereact` | `^11.0.0` | peer of `@cratis/components@3.0.0` |
17
+ | `@cratis/components` | `4.6.0` | its package manifest, `Source/CommandDialog/*` (`StepperCommandDialog`, `CommandStepper`, `StepperPanel`), `Documentation/StepperCommandDialog/*`, `Documentation/CommandStepper/*` |
18
+ | `@cratis/arc.react` | `>=20.3.1 <23` | peer range declared by `@cratis/components@4.6.0` |
20
19
 
21
20
  ## Choose it over `CommandDialog` when
22
21
 
@@ -84,7 +83,7 @@ import { useDialog } from '@cratis/arc.react/dialogs';
84
83
 
85
84
  const [CreateProjectWrapper, showCreateProject] = useDialog(CreateProjectDialog);
86
85
 
87
- <MenuItem label='New project' icon={() => <i className='pi pi-plus' />} command={() => showCreateProject()} />
86
+ <MenuItem label='New project' icon={FaPlus} command={() => showCreateProject()} /> // icon is a component type, e.g. from 'react-icons/fa6'
88
87
  <CreateProjectWrapper />
89
88
  ```
90
89
 
@@ -155,8 +154,6 @@ Stepper-level props (inherited from the stepper customization surface):
155
154
  | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` |
156
155
  | `headerPosition` | `'top' \| 'bottom'` | `'top'` |
157
156
  | `linear` | `boolean` | `true` |
158
- | `showNavigation` | `boolean` | `true` |
159
- | `showSubmit` | `boolean` | `true` |
160
157
  | `start` / `end` | `React.ReactNode` | extra content beside the step headers |
161
158
  | `onChangeStep` | `(event: { index: number }) => void` | — |
162
159
 
@@ -164,12 +161,14 @@ Command-form props (`initialValues`, `currentValues`, `validateOn`,
164
161
  `validateOnInit`, `onSuccess`, `onValidationFailure`, `onFailed`,
165
162
  `autoServerValidate`, …) all apply as well.
166
163
 
167
- ### Two pass-through targets
164
+ ### Two part targets
168
165
 
169
- The inherited `pt`, `ptOptions`, and `unstyled` target the **inner stepper**.
170
- Use `dialogPt`, `dialogPtOptions`, `dialogUnstyled`, and `dialogClassName` to
171
- reach the **outer dialog**. Getting these the wrong way round is the usual cause
172
- of a pass-through that appears to do nothing.
166
+ The inherited `pt` (`StepperParts`: `root`, `list`, `step`, `header`, `number`,
167
+ `title`, `separator`, `panels`, `panel`) targets the **inner stepper**. Use
168
+ `dialogPt` (`DialogParts`) and `dialogClassName` to reach the **outer dialog**.
169
+ Getting these the wrong way round is the usual cause of a `pt` that appears to do
170
+ nothing. (`ptOptions`/`unstyled` and their `dialog*` twins are accepted for
171
+ Components 3 source compatibility and do nothing.)
173
172
 
174
173
  ## Dismissal while the command runs
175
174
 
@@ -193,7 +192,7 @@ children.
193
192
  | The same property bound on two steps | Each property appears on exactly one step |
194
193
  | A `StepperPanel` without `header` | `header` is the navigation label |
195
194
  | Several panels wrapped in one fragment | A fragment counts as **one** step — give each step its own `StepperPanel` child |
196
- | A raw PrimeReact control for a command value | Use a `CommandForm` field, or validation never re-runs |
195
+ | A raw control (`TextInput`, a native `<input>`) for a command value | Use a `CommandForm` field, or the value is never bound and validation never re-runs |
197
196
  | Seeding a required value in `onBeforeExecute` | Use `initialValues` |
198
197
  | `pt` applied expecting it to reach the dialog | `pt` targets the stepper; use `dialogPt` |
199
198