@cratis/pi 0.0.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 (125) hide show
  1. package/README.md +43 -0
  2. package/package.json +24 -0
  3. package/skills/cratis-application-react-specifications/LICENSE +2 -0
  4. package/skills/cratis-application-react-specifications/SKILL.md +192 -0
  5. package/skills/cratis-application-slice-specifications/LICENSE +2 -0
  6. package/skills/cratis-application-slice-specifications/SKILL.md +222 -0
  7. package/skills/cratis-arc-authentication-authorization-and-identity/LICENSE +2 -0
  8. package/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +233 -0
  9. package/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +140 -0
  10. package/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +112 -0
  11. package/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +102 -0
  12. package/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +85 -0
  13. package/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +87 -0
  14. package/skills/cratis-arc-command/LICENSE +2 -0
  15. package/skills/cratis-arc-command/SKILL.md +286 -0
  16. package/skills/cratis-arc-command/references/command-result.md +111 -0
  17. package/skills/cratis-arc-command/references/handler-shapes.md +135 -0
  18. package/skills/cratis-arc-command/references/proxy-generation.md +87 -0
  19. package/skills/cratis-arc-command/references/read-model-injection.md +97 -0
  20. package/skills/cratis-arc-command-execution/LICENSE +2 -0
  21. package/skills/cratis-arc-command-execution/SKILL.md +148 -0
  22. package/skills/cratis-arc-command-validation/LICENSE +2 -0
  23. package/skills/cratis-arc-command-validation/SKILL.md +230 -0
  24. package/skills/cratis-arc-ef-core-migration/LICENSE +2 -0
  25. package/skills/cratis-arc-ef-core-migration/SKILL.md +231 -0
  26. package/skills/cratis-arc-observable-query-http/LICENSE +2 -0
  27. package/skills/cratis-arc-observable-query-http/SKILL.md +157 -0
  28. package/skills/cratis-arc-query-paging/LICENSE +2 -0
  29. package/skills/cratis-arc-query-paging/SKILL.md +173 -0
  30. package/skills/cratis-arc-react-page/LICENSE +2 -0
  31. package/skills/cratis-arc-react-page/SKILL.md +291 -0
  32. package/skills/cratis-arc-react-page/references/data-page.md +125 -0
  33. package/skills/cratis-arc-react-page/references/data-tables.md +136 -0
  34. package/skills/cratis-arc-react-page/references/dialogs.md +238 -0
  35. package/skills/cratis-arc-react-page/references/mvvm.md +150 -0
  36. package/skills/cratis-arc-react-page/references/queries-and-commands.md +200 -0
  37. package/skills/cratis-chronicle-cli-operations/LICENSE +2 -0
  38. package/skills/cratis-chronicle-cli-operations/SKILL.md +199 -0
  39. package/skills/cratis-chronicle-client-dotnet/LICENSE +2 -0
  40. package/skills/cratis-chronicle-client-dotnet/SKILL.md +433 -0
  41. package/skills/cratis-chronicle-client-elixir/LICENSE +2 -0
  42. package/skills/cratis-chronicle-client-elixir/SKILL.md +429 -0
  43. package/skills/cratis-chronicle-client-kotlin/LICENSE +2 -0
  44. package/skills/cratis-chronicle-client-kotlin/SKILL.md +498 -0
  45. package/skills/cratis-chronicle-client-typescript/LICENSE +2 -0
  46. package/skills/cratis-chronicle-client-typescript/SKILL.md +396 -0
  47. package/skills/cratis-chronicle-compliance/LICENSE +2 -0
  48. package/skills/cratis-chronicle-compliance/SKILL.md +246 -0
  49. package/skills/cratis-chronicle-event-constraints/LICENSE +2 -0
  50. package/skills/cratis-chronicle-event-constraints/SKILL.md +227 -0
  51. package/skills/cratis-chronicle-event-modeling/LICENSE +2 -0
  52. package/skills/cratis-chronicle-event-modeling/SKILL.md +157 -0
  53. package/skills/cratis-chronicle-event-specifications/LICENSE +2 -0
  54. package/skills/cratis-chronicle-event-specifications/SKILL.md +200 -0
  55. package/skills/cratis-chronicle-event-type-migration/LICENSE +2 -0
  56. package/skills/cratis-chronicle-event-type-migration/SKILL.md +235 -0
  57. package/skills/cratis-chronicle-mcp-inspection/LICENSE +2 -0
  58. package/skills/cratis-chronicle-mcp-inspection/SKILL.md +79 -0
  59. package/skills/cratis-chronicle-mcp-inspection/references/blocked-tools.md +12 -0
  60. package/skills/cratis-chronicle-mcp-inspection/references/observational-tools.md +8 -0
  61. package/skills/cratis-chronicle-multi-tenancy/LICENSE +2 -0
  62. package/skills/cratis-chronicle-multi-tenancy/SKILL.md +198 -0
  63. package/skills/cratis-chronicle-projection/LICENSE +2 -0
  64. package/skills/cratis-chronicle-projection/SKILL.md +162 -0
  65. package/skills/cratis-chronicle-projection/references/fluent-builder.md +126 -0
  66. package/skills/cratis-chronicle-projection/references/model-bound-attributes.md +118 -0
  67. package/skills/cratis-chronicle-reactor/LICENSE +2 -0
  68. package/skills/cratis-chronicle-reactor/SKILL.md +344 -0
  69. package/skills/cratis-chronicle-read-model/LICENSE +2 -0
  70. package/skills/cratis-chronicle-read-model/SKILL.md +193 -0
  71. package/skills/cratis-chronicle-read-model/references/queries.md +142 -0
  72. package/skills/cratis-chronicle-read-model-specifications/LICENSE +2 -0
  73. package/skills/cratis-chronicle-read-model-specifications/SKILL.md +176 -0
  74. package/skills/cratis-chronicle-reducer/LICENSE +2 -0
  75. package/skills/cratis-chronicle-reducer/SKILL.md +173 -0
  76. package/skills/cratis-chronicle-web-workbench/LICENSE +2 -0
  77. package/skills/cratis-chronicle-web-workbench/SKILL.md +177 -0
  78. package/skills/cratis-cli-terminal-workbench/LICENSE +2 -0
  79. package/skills/cratis-cli-terminal-workbench/SKILL.md +156 -0
  80. package/skills/cratis-code-review/LICENSE +2 -0
  81. package/skills/cratis-code-review/SKILL.md +177 -0
  82. package/skills/cratis-components-accessibility/LICENSE +2 -0
  83. package/skills/cratis-components-accessibility/SKILL.md +176 -0
  84. package/skills/cratis-components-schema-editor/LICENSE +2 -0
  85. package/skills/cratis-components-schema-editor/SKILL.md +238 -0
  86. package/skills/cratis-components-stepper-command-dialog/LICENSE +2 -0
  87. package/skills/cratis-components-stepper-command-dialog/SKILL.md +209 -0
  88. package/skills/cratis-components-styling/LICENSE +2 -0
  89. package/skills/cratis-components-styling/SKILL.md +221 -0
  90. package/skills/cratis-components-toolbar/LICENSE +2 -0
  91. package/skills/cratis-components-toolbar/SKILL.md +259 -0
  92. package/skills/cratis-documentation-writing/LICENSE +2 -0
  93. package/skills/cratis-documentation-writing/SKILL.md +121 -0
  94. package/skills/cratis-event-model-diagram/LICENSE +2 -0
  95. package/skills/cratis-event-model-diagram/SKILL.md +142 -0
  96. package/skills/cratis-fundamentals-concept/LICENSE +2 -0
  97. package/skills/cratis-fundamentals-concept/SKILL.md +198 -0
  98. package/skills/cratis-fundamentals-type-discovery/LICENSE +2 -0
  99. package/skills/cratis-fundamentals-type-discovery/SKILL.md +152 -0
  100. package/skills/cratis-governed-release-methodology/LICENSE +2 -0
  101. package/skills/cratis-governed-release-methodology/SKILL.md +297 -0
  102. package/skills/cratis-lens-browser-extension/LICENSE +2 -0
  103. package/skills/cratis-lens-browser-extension/SKILL.md +239 -0
  104. package/skills/cratis-performance-review/LICENSE +2 -0
  105. package/skills/cratis-performance-review/SKILL.md +139 -0
  106. package/skills/cratis-screenplay-model-authoring/LICENSE +2 -0
  107. package/skills/cratis-screenplay-model-authoring/SKILL.md +231 -0
  108. package/skills/cratis-security-review/LICENSE +2 -0
  109. package/skills/cratis-security-review/SKILL.md +141 -0
  110. package/skills/cratis-specification-by-example/LICENSE +2 -0
  111. package/skills/cratis-specification-by-example/SKILL.md +188 -0
  112. package/skills/cratis-specifications-csharp/LICENSE +2 -0
  113. package/skills/cratis-specifications-csharp/SKILL.md +230 -0
  114. package/skills/cratis-specifications-csharp/references/application-scenarios.md +191 -0
  115. package/skills/cratis-specifications-csharp/references/csharp-patterns.md +142 -0
  116. package/skills/cratis-specifications-csharp/references/integration-specs.md +126 -0
  117. package/skills/cratis-specifications-typescript/LICENSE +2 -0
  118. package/skills/cratis-specifications-typescript/SKILL.md +202 -0
  119. package/skills/cratis-specifications-typescript/references/typescript-patterns.md +168 -0
  120. package/skills/cratis-stage-rendering-and-sandbox/LICENSE +2 -0
  121. package/skills/cratis-stage-rendering-and-sandbox/SKILL.md +230 -0
  122. package/skills/cratis-studio-mcp-safety-guidance/LICENSE +2 -0
  123. package/skills/cratis-studio-mcp-safety-guidance/SKILL.md +69 -0
  124. package/skills/cratis-studio-mcp-safety-guidance/references/blocked-tools.md +12 -0
  125. package/skills/cratis-studio-mcp-safety-guidance/references/observational-tools.md +8 -0
@@ -0,0 +1,239 @@
1
+ ---
2
+ name: cratis-lens-browser-extension
3
+ description: Use the Cratis Lens browser extension against a running Cratis Arc application — building and side-loading it, what the Arc app must already serve for Lens to work, switching the active user and tenant, and executing a command or query from the popup. Use when setting Lens up, when Lens does not detect an Arc app, or when a switched identity or tenant does not take effect. Do not use for production authentication or authorization design, and do not use for Chronicle inspection.
4
+ license: MIT
5
+ ---
6
+
7
+ # Use Lens against a running Arc application
8
+
9
+ Lens is a Manifest V3 browser extension that impersonates a user and a tenant
10
+ against an Arc application you are developing, and executes that application's
11
+ commands and queries from a popup. It changes nothing in the application: it
12
+ rewrites request headers at the network boundary and reads Arc's own
13
+ introspection endpoints.
14
+
15
+ ⚠️ Lens is a **development** tool that can present any identity to any origin
16
+ the developer points it at. Nothing in it is production tooling.
17
+
18
+ ## Verified product sources
19
+
20
+ | Source | Version | Purpose |
21
+ | --- | --- | --- |
22
+ | `Cratis/Lens` `Source/` | manifest `1.0.0`, repository revision `3d0df87` | The extension itself |
23
+ | `Cratis.Arc.Core` | `22.10.4` | The `/.cratis/*` endpoints, the forwarded-identity contract, and the tenancy header Lens depends on |
24
+
25
+ ⚠️ **There is no published store listing.** Nothing in the Lens repository links
26
+ a Chrome Web Store, Edge Add-ons, AMO, or App Store entry — the only store URLs
27
+ present are developer-console links inside its own publishing guide. Building
28
+ from source and loading unpacked is the only install path that exists.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ cd Source
34
+ yarn install
35
+ yarn build # writes the unpacked extension to Source/dist/
36
+ ```
37
+
38
+ Then open `chrome://extensions`, enable **Developer mode**, choose **Load
39
+ unpacked**, and select `Source/dist`. Chrome lists it as **Cratis Lens** — the
40
+ name in `manifest.json`; the repository's own tutorial calls it "Lens - Cratis
41
+ Developer Tools", which is stale.
42
+
43
+ `yarn dev` is `vite build --watch`; the extension still has to be reloaded in
44
+ Chrome after each rebuild. `yarn ci` runs `typecheck`, `test` and `build` — the
45
+ same three the pull-request workflow runs. There is no lint script.
46
+
47
+ The code uses the `chrome.*` namespace throughout with no polyfill. The release
48
+ workflow also packages for Edge, Firefox and Safari, but no cross-browser
49
+ behavior is tested; treat Chromium as the supported target.
50
+
51
+ ## What the Arc application must already provide
52
+
53
+ Lens is not self-contained. It reads endpoints Arc maps for itself, and it sends
54
+ headers Arc's shipped identity handler already reads. **No NuGet package,
55
+ middleware, `Program.cs` change, or configuration key exists for "Lens support"
56
+ — nothing in either repository names one.**
57
+
58
+ | Route | Mapped by | Lens uses it for |
59
+ | --- | --- | --- |
60
+ | `GET /.cratis/commands` | `MapIntrospectionEndpoints`, `AllowAnonymous` | The Commands tree |
61
+ | `GET /.cratis/queries` | `MapIntrospectionEndpoints`, `AllowAnonymous` | The Queries tree |
62
+ | `GET /.cratis/users` | every `ICanProvideUsers`, `AllowAnonymous` | The importable user roster |
63
+ | `GET /.cratis/tenants` | every `ICanProvideTenants`, `AllowAnonymous` | The importable tenant list |
64
+ | `GET /.cratis/identity-details/schema` | the identity endpoint mapper | The schema-driven editor for a user's `identityDetails` |
65
+
66
+ `MapIntrospectionEndpoints` is called from Arc's own application startup, so the
67
+ two introspection routes exist without being asked for. The user and tenant
68
+ routes return whatever the application implements — with **no implementation
69
+ registered they return an empty list**, and Lens treats a `404` on either as
70
+ "empty", not "broken". Users and tenants can also be created by hand in Lens's
71
+ Settings, so an application that provides neither still works.
72
+
73
+ ⚠️ `/.cratis/users` and `/.cratis/tenants` are mapped `AllowAnonymous` and
74
+ **unconditionally**. Register `ICanProvideUsers`/`ICanProvideTenants` only for a
75
+ development environment and return fixtures — see
76
+ `cratis-arc-authentication-authorization-and-identity`.
77
+
78
+ ### Identity
79
+
80
+ Lens sends exactly the three forwarded client-principal headers Arc's shipped
81
+ handler reads:
82
+
83
+ ```text
84
+ X-MS-CLIENT-PRINCIPAL-ID
85
+ X-MS-CLIENT-PRINCIPAL-NAME
86
+ X-MS-CLIENT-PRINCIPAL # base64 of the principal JSON
87
+ ```
88
+
89
+ The principal document it base64-encodes is
90
+ `{ identityProvider, userId, userDetails, userRoles, claims: [{ typ, val }], … }`
91
+ with the user's `identityDetails` and `applicationProperties` spread over it.
92
+ `identityProvider` defaults to `aad` and `userRoles` falls back to
93
+ `["authenticated", "anonymous"]` when the user has no roles.
94
+
95
+ ⚠️ That is Lens's **only** identity mode. There is no bearer token, no OIDC, no
96
+ sign-in. The application has to be running on the Microsoft-Identity-Platform
97
+ forwarded-header path, and the reverse proxy in front of it in production must
98
+ be the only thing that can set those headers.
99
+
100
+ ### Tenancy
101
+
102
+ Lens sets one header, named by its `tenantHeaderName` setting, whose default is
103
+ `x-cratis-tenant-id`. That matches `TenancyOptions.HttpHeader`'s default, and
104
+ `TenantResolverType.Header` is Arc's default resolver — so a default Arc
105
+ application needs no change.
106
+
107
+ ⚠️ **`tenantHeaderName` has no user interface.** It is a stored setting with a
108
+ default and nothing binds it to an input. An application that resolves tenancy
109
+ by query parameter, claim, subdomain, or a renamed header **cannot be driven by
110
+ Lens today** without editing `chrome.storage.local` by hand. Guidance describing
111
+ a "Tenant Header Name" field on an options page is describing something that
112
+ does not exist — there is no `options_page` in the manifest at all.
113
+
114
+ ### The frontend
115
+
116
+ Detection is not header-level and is not invisible. Lens injects a function into
117
+ the page's main world and looks for:
118
+
119
+ 1. an element with `id="root"`;
120
+ 2. a React fiber or container key on it (`__reactFiber…` / `__reactContainer…`);
121
+ 3. a context value on the fiber tree carrying either a `reconnectQueries`
122
+ property, or a `configuration` object with one of `baseUrl`, `apiBaseUrl`,
123
+ `apiSurface`, `apiSurfaceBaseUrl`, `baseUri` or `baseAddress`.
124
+
125
+ If that fails it falls back to matching the path
126
+ `/projects/{guid}/application/{guid}`, and otherwise to the host being
127
+ `localhost` or `127.0.0.1`.
128
+
129
+ ⚠️ A non-React frontend, a root element with another id, or a React version that
130
+ renames its internal fiber keys leaves Lens undetected — the Commands, Queries
131
+ and Query Diagnostics tabs are then **disabled**, and only Context and Settings
132
+ work. The detected base URL is also what the Commands and Queries tabs call, so
133
+ without detection there is nothing to call.
134
+
135
+ ## Switch the active user or tenant
136
+
137
+ Pick both on the **Context** tab. Two things then happen, in this order:
138
+
139
+ 1. The service worker rebuilds its `declarativeNetRequest` **dynamic** rules —
140
+ at most two, one for the identity headers and one for the tenant header.
141
+ 2. It deletes the Arc identity cookie `.cratis-identity` on the detected base URL
142
+ and page origins, then reloads every open tab on those origins.
143
+
144
+ Step 2 is the one that matters. Headers alone do not change who you are once Arc
145
+ has issued its identity cookie; the application keeps presenting the identity it
146
+ was first given. Dropping the cookie and reloading is what makes the switch take
147
+ effect.
148
+
149
+ The rules are scoped, deliberately. When a page origin is known the condition is
150
+ `{ urlFilter: '*', initiatorDomains: [<page host>] }`; otherwise it is
151
+ `||<base URL host>`. **With neither resolvable, no rules are installed at all** —
152
+ `host_permissions` is `<all_urls>`, so a wildcard filter would attach
153
+ impersonation headers to every XHR on every site the developer browses.
154
+
155
+ ⚠️ The rules match `XMLHTTPREQUEST` **only**. Document navigations, WebSocket
156
+ connections and EventSource/SSE streams carry no injected headers. Arc's
157
+ observable-query transports (`/.cratis/queries/ws`, `/.cratis/queries/sse`) are
158
+ therefore **not** covered by header injection.
159
+
160
+ ## Execute a command or query
161
+
162
+ The Commands and Queries tabs read the introspection lists and group them into a
163
+ tree by splitting the namespace on `.`.
164
+
165
+ A command is always `POST <base URL><route>` with `Content-Type: application/json`
166
+ and the active context headers, and a body built by the schema-driven editor. A
167
+ query is always `GET`, with `{name}` segments in the route substituted from the
168
+ path-parameter inputs and the same context headers.
169
+
170
+ ⚠️ **The schema-driven payload form does not currently receive a schema from
171
+ Arc.** Lens reads an optional property named `schema` off each introspection
172
+ item, and falls back to trying `/.cratis/types/{type}`, `/.cratis/schema/{type}`,
173
+ `/.cratis/schemas/{type}` and `/.cratis/types?type={type}` in turn. Arc names the
174
+ property **`payloadSchema`** on a command and **`argumentsSchema`** on a query,
175
+ and maps none of those four fallback routes. Every branch therefore misses, and
176
+ the panel shows *"No payload schema available. Lens will execute this command
177
+ with an empty payload object."*
178
+
179
+ There is **no raw-JSON editor** behind that empty state, so a command reached
180
+ this way can only be fired with `{}`. Expect that for every command until the two
181
+ sides agree on a property name; do not read it as a broken application. Queries
182
+ are unaffected — their inputs come from the `{name}` segments of the route, not
183
+ from a schema.
184
+
185
+ Both result panels parse defensively rather than against a contract: success is
186
+ `isSuccess` when present and HTTP 2xx otherwise, messages are harvested from
187
+ `exceptionMessages`/`errors`/`message`/`title`/`detail`, and a query payload is
188
+ unwrapped from the first of `data`, `result`, `items`, `results`, `value`,
189
+ `payload` or `content` that is present.
190
+
191
+ ⚠️ **The two introspection fetches send no headers and no credentials**, while
192
+ the Settings user/tenant refresh does send both. So an application that puts
193
+ authorization in front of `/.cratis/commands` breaks the Commands and Queries
194
+ tabs while Settings keeps working — which reads as a mysterious partial failure.
195
+ Leave those two routes anonymous.
196
+
197
+ Queries are path-parameter GETs only: no query-string arguments, no paging or
198
+ sorting inputs, and no observable subscription from the popup.
199
+
200
+ ## Query diagnostics
201
+
202
+ The Query Diagnostics tab is read-only and polls every 2 seconds. It requires the
203
+ detected Arc context to expose `observableQueryDiagnostics.getSnapshot()`; when
204
+ it does not, the panel reports no data. Nothing else in Lens depends on it.
205
+
206
+ ## Safety
207
+
208
+ The manifest asks for `storage`, `scripting`, `cookies`, `declarativeNetRequest`,
209
+ `declarativeNetRequestWithHostAccess` and `<all_urls>`. That combination lets the
210
+ extension present a chosen identity to any origin, read and delete cookies, and
211
+ inject into any page.
212
+
213
+ - Keep it out of a browser profile used for anything but development.
214
+ - The user roster, including claims, is stored unencrypted in
215
+ `chrome.storage.local`.
216
+ - An application reachable outside a developer's machine while trusting the
217
+ forwarded headers is impersonatable by anyone who can set them. Lens does not
218
+ create that exposure — it demonstrates it.
219
+
220
+ ## Verify
221
+
222
+ - `yarn ci` is clean, and Chrome loaded `Source/dist` without errors.
223
+ - The application answers `/.cratis/commands` and `/.cratis/queries`
224
+ anonymously.
225
+ - Tenancy resolves from the header and the header is named
226
+ `x-cratis-tenant-id`.
227
+ - Opening the popup on the app's tab enables the Commands tab — that is the
228
+ detection signal.
229
+ - After switching a user, the tab reloaded and the application reports the new
230
+ identity, not the previous one.
231
+ - Development user and tenant providers, if registered at all, return fixtures.
232
+
233
+ ## Route near misses
234
+
235
+ - Designing authentication, authorization, identity details or tenancy in the
236
+ application: `cratis-arc-authentication-authorization-and-identity`.
237
+ - Calling a command over HTTP without the extension: `cratis-arc-command-execution`.
238
+ - Observable query transports and their HTTP shapes: `cratis-arc-observable-query-http`.
239
+ - Inspecting a Chronicle event store: the Chronicle CLI or Workbench guidance.
@@ -0,0 +1,2 @@
1
+ Copyright (c) Cratis. All rights reserved.
2
+ Licensed under the MIT license. See LICENSE file in the project root for full license information.
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: cratis-performance-review
3
+ description: Perform a focused scalability review of changed code in a Cratis application — Chronicle observers and replay, read-model query shape, command and query payloads, .NET enumeration, and React render cost — and report findings by risk. Use when asked to check for performance or scalability problems. Do not use for ordinary implementation and do not override the authoritative paging guidance.
4
+ license: MIT
5
+ ---
6
+
7
+ # Cratis performance review
8
+
9
+ Most performance findings in a Cratis application are shape problems, not
10
+ hot-loop problems: a projection that has to re-read, a query that materializes
11
+ before it filters, a payload nobody uses. They are cheap to fix while the change
12
+ is open and expensive once data has grown behind them.
13
+
14
+ Two things make this stack different. **A projection must be able to replay the
15
+ entire history** — a cost that is invisible at development volumes and fatal at
16
+ production ones. And **an event is permanent**, so an oversized event is a
17
+ storage and replay cost that never goes away.
18
+
19
+ ## Verified product sources
20
+
21
+ | Package | Version | Purpose |
22
+ | --- | --- | --- |
23
+ | `Cratis.Chronicle` | `16.45.2` | Observers, projections, reducers, reactors, replay |
24
+ | `Cratis.Arc.Core` | `22.10.4` | Query return shapes and server-side paging |
25
+ | `@cratis/components` | current | `DataTable` paging surface |
26
+
27
+ Reverify against the owning product repository before asserting a framework
28
+ behavior this file does not already state.
29
+
30
+ ## Route near misses
31
+
32
+ - General correctness and maintainability: use `cratis-code-review`. Its
33
+ performance section is the pass-by list; this skill is the focused one. When
34
+ both have run, do not restate the same finding twice.
35
+ - Adding paging to a query rather than judging one: use
36
+ `cratis-arc-query-paging`. That skill is authoritative on the mechanism; this
37
+ one does not override it.
38
+ - Security or data exposure: use `cratis-security-review`. An over-fetching
39
+ payload is a finding for both — say which lens produced it.
40
+
41
+ ## Step 1 — Chronicle and event sourcing
42
+
43
+ - **A projection joins on events, never on a read model.** A read-model join
44
+ forces a re-read the projection engine cannot optimize, and it makes the
45
+ projection depend on another observer's position.
46
+ - **A reactor does not re-query the event log inside its handler.** The event it
47
+ received already carries what it needs. (Reactor dispatch is by the handler's
48
+ first parameter type — the method name is free, so look for the event-typed
49
+ parameter, not for a method called `On`.)
50
+ - **A new projection can replay all historical events without failing.** Ask it
51
+ explicitly: at production volume, does this projection complete a full replay?
52
+ - **Events are small.** No large blob and no base64 payload embedded in an
53
+ event; put the content elsewhere and carry a reference.
54
+ - No eager load of a whole event sequence without paging or filtering.
55
+ - AutoMap is on by default. Hand-mapping every property is both a maintenance
56
+ and a cost problem — and, since AutoMap runs anyway, it does not save the work
57
+ it appears to.
58
+
59
+ ## Step 2 — Read models and their store
60
+
61
+ - Queries filter on indexed fields. An unintentional full-collection scan is the
62
+ most common finding here.
63
+ - **A list that can grow returns `IQueryable<T>`**, so Arc applies server-side
64
+ paging and sorting. Materializing and then slicing in memory reads the whole
65
+ collection every request.
66
+ - No N+1: one query returns what the caller needs.
67
+ - A count is a count. Hydrating the collection to measure its length is the
68
+ same finding as an unpaged list, wearing different clothes.
69
+ - A read model does not embed a large nested collection nothing fully iterates.
70
+
71
+ ## Step 3 — Commands and queries
72
+
73
+ - A response payload carries only fields the client uses.
74
+ - Command validators are synchronous and in-memory. Validation is on the hot
75
+ path of every attempt, including the ones that will be rejected — an I/O call
76
+ there is paid on every request.
77
+ - No `await Task.Run(() => syncWork)` wrapping for work that is naturally
78
+ asynchronous.
79
+
80
+ ## Step 4 — .NET
81
+
82
+ - No `.ToList()` before `.Where()`. Filter before materializing.
83
+ - An `IEnumerable<T>` is not enumerated more than once — materialize once when
84
+ it must be reused.
85
+ - Large-object logging uses the destructuring form only at `Debug` level, so a
86
+ production log level does not pay to serialize it.
87
+
88
+ ## Step 5 — React
89
+
90
+ - A `DataTable` over a growable collection uses lazy loading and a paginator
91
+ rather than rendering every row.
92
+ - No inline object or array literal passed as a prop: it is a new identity every
93
+ render and defeats memoization downstream.
94
+ - `useEffect` dependencies are correct — neither missing nor over-broad. An
95
+ over-broad dependency array is a re-run per render, which reads as a
96
+ correctness bug and behaves as a performance one.
97
+ - Components over large collections hold stable references or are memoized.
98
+ - No `JSON.parse(JSON.stringify(x))` deep cloning.
99
+
100
+ ## Step 6 — Report
101
+
102
+ Open with one line:
103
+
104
+ > **Performance review: No issues / Minor findings / Blocking issues found**
105
+
106
+ Group findings by the section that produced them, and classify each:
107
+
108
+ | Risk | Meaning |
109
+ | --- | --- |
110
+ | **High** | Measurable degradation at moderate load — fix before merge |
111
+ | **Medium** | Degrades under load or as data grows |
112
+ | **Low** | Minor inefficiency |
113
+
114
+ Close with a per-section summary table.
115
+
116
+ **Say what the finding costs and at what scale.** "This is slow" is not
117
+ actionable; "this scans the whole collection on every page load, so it is fine
118
+ at hundreds of rows and not at tens of thousands" is. And **name what you did
119
+ not measure** — a review of code shape is not a benchmark, and reporting it as
120
+ one overstates the evidence.
121
+
122
+ ## What breaks
123
+
124
+ - **A shape finding is reported as a measurement.** Reading code tells you the
125
+ shape; only running it tells you the cost. Say which one you have.
126
+ - **The paging advice contradicts the paging skill.** `cratis-arc-query-paging`
127
+ is authoritative on the mechanism. Report the missing paging; do not invent an
128
+ alternative to it.
129
+ - **A replay cost is judged at development volume.** A projection over a hundred
130
+ seeded events proves nothing about a replay over a production history.
131
+ - **Every finding is High.** The classification is the value; when all findings
132
+ are urgent, none are.
133
+
134
+ ## How it is proven
135
+
136
+ The build and specifications are green before the report is written; each
137
+ finding cites the file and line and states the scale at which it bites; and the
138
+ report says plainly that it reviewed code shape rather than measured behavior,
139
+ naming anything that would need a benchmark to settle.
@@ -0,0 +1,2 @@
1
+ Copyright (c) Cratis. All rights reserved.
2
+ Licensed under the MIT license. See LICENSE file in the project root for full license information.
@@ -0,0 +1,231 @@
1
+ ---
2
+ name: cratis-screenplay-model-authoring
3
+ description: Author and verify a Cratis Screenplay `.play` event model — the file shape, the constructs the compiler actually accepts, the `screenplay` dotnet tool, and the gap between what parses and what any downstream runtime admits. Use when writing, reviewing, or compiling a `.play` file, or when deciding whether a construct is safe to model. Do not use for rendering a model into an application, and do not use for hand-written Arc or Chronicle C#.
4
+ license: MIT
5
+ ---
6
+
7
+ # Author a Screenplay `.play` model
8
+
9
+ Screenplay is a declarative, indentation-based language that describes a whole
10
+ event-sourced CQRS system in one document set: concepts, events, commands,
11
+ queries, projections, screens and specifications. This repository owns the
12
+ language and its compiler front end.
13
+
14
+ ⚠️ **Screenplay is experimental, and it is a front end.** It parses, validates
15
+ and prints `.play` source, and binds a deliberately narrow subset to a versioned
16
+ executable semantic model. **It does not generate, render or run an
17
+ application.** Nothing in it emits C#, TypeScript or any target artifact — that
18
+ is Stage's job, and Stage admits less than Screenplay parses. Author against that
19
+ reality, not against the ecosystem diagram.
20
+
21
+ ## Verified product sources
22
+
23
+ | Package | Version | Purpose |
24
+ | --- | --- | --- |
25
+ | `Cratis.Screenplay` | `4.12.1` | The compiler: parser, syntax tree, validator, diagnostics, printer, semantic model |
26
+ | `Cratis.Screenplay.Tool` | `4.12.1` | The `screenplay` dotnet tool |
27
+ | `Cratis.Screenplay.CanonicalCorpus` | `4.12.1` | The frozen `RegisterProject` conformance vector downstream tools verify against |
28
+
29
+ Behavior below is read from the repository at revision `7e2d91d`. Reverify before
30
+ claiming another version behaves the same.
31
+
32
+ ## Verify a model
33
+
34
+ ```shell
35
+ dotnet tool install -g Cratis.Screenplay.Tool
36
+ screenplay # or: screenplay path/to/model
37
+ ```
38
+
39
+ The tool takes **one positional argument** — a directory or a single `.play`
40
+ file, defaulting to the current directory — plus `--no-color` and
41
+ `--warnaserror`. That is the entire surface: no verbs, no `--help`, no
42
+ `--version`, and no output or emit flag.
43
+
44
+ ⚠️ **It only verifies. It never writes anything.** A tool invocation that appears
45
+ to have "generated" something did not.
46
+
47
+ A directory is compiled as **one application**, recursively over `**/*.play`, so
48
+ a name declared in one file resolves from another. A single file is compiled
49
+ alone. Exit code is `1` when there is any error, or any warning under
50
+ `--warnaserror`; otherwise `0`. Diagnostics print compiler-style with the
51
+ offending line and a caret:
52
+
53
+ ```text
54
+ <file>(<line>,<column>): error PLAY0042: <message>
55
+ 42 | <the offending line>
56
+ | ^
57
+ ```
58
+
59
+ Codes run `PLAY0001` through `PLAY0276`. Embed the same compiler with
60
+ `dotnet add package Cratis.Screenplay`.
61
+
62
+ ## The file shape
63
+
64
+ Plain text, UTF-8, `.play`. Nesting is by **indentation** — no braces, no
65
+ terminators. A construct owns everything indented beneath it. `//` starts a
66
+ comment. Reserved words inside a block are escaped with a leading `@`.
67
+
68
+ ## What the compiler accepts
69
+
70
+ These are the constructs the parser dispatches on, not a documentation summary.
71
+
72
+ **Top level:** `domain`, `import`, `concept`, `type`, `policy`, `persona`,
73
+ `authentication`, `module`, `seed`, `trigger`, `ui profile`, `theme`, `layout`.
74
+ Anything else is `Unexpected '<word>' at the top level`.
75
+
76
+ **Inside `module`:** `description`, `screen` (a screen template), `dialog` (a
77
+ dialog template), `form`, `contribute`, `feature`.
78
+
79
+ **Inside `feature`:** `description`, `feature` (nested to any depth), `slice`,
80
+ `contribute`.
81
+
82
+ **`slice <Type> <Name>`** takes one of four types — an unknown one is
83
+ `Unknown slice type '<x>' - expected StateChange, StateView, Automation or
84
+ Translate`:
85
+
86
+ | Slice type | What it models | Constructs it carries |
87
+ | --- | --- | --- |
88
+ | `StateChange` | something changes the system | `command` → `event` via `produces`, with `validate`, `authorize`, `constraint` |
89
+ | `StateView` | something reads the system | `readmodel`, `projection`, `query`, `screen` |
90
+ | `Automation` | something runs when something happens | `reaction` |
91
+ | `Translate` | outside data becomes events | `capture` |
92
+
93
+ **Inside any slice:** `description`, `event`, `command`, `query`, `projection`,
94
+ `capture`, `reaction`, `screen`, `constraint`, `specification`, `readmodel`,
95
+ `reducer`. An unrecognised word here is a **warning**, not an error, and its
96
+ block is skipped — a typo can silently drop a whole construct, so treat slice
97
+ warnings as failures.
98
+
99
+ A `concept` is based on one of `Uuid`, `String`, `Int`, `Decimal`, `Bool`,
100
+ `Date`, `DateTime`, or `Enum` with its values indented beneath. Concepts carry
101
+ `@pii` and `@sensitive` attributes and their own `validate` block, and every use
102
+ of the concept inherits them.
103
+
104
+ Projections are written in the Projection Declaration Language and captures in
105
+ the Change Data Capture Language; both are sub-grammars of the same file, parsed
106
+ by dedicated parsers rather than passed through as text.
107
+
108
+ ## A complete model
109
+
110
+ This is the canonical conformance vector — the smallest complete program the
111
+ whole toolchain is verified against, quoted from
112
+ `Screenplay.CanonicalCorpus`'s `RegisterProject` source:
113
+
114
+ ```screenplay
115
+ concept ProjectId : Uuid
116
+ concept ProjectName : String
117
+ module Projects
118
+ feature Registration
119
+ slice StateChange RegisterProject
120
+ command RegisterProject
121
+ projectId ProjectId identifier
122
+ name ProjectName
123
+ validate
124
+ name not empty message "Project name is required"
125
+ produces ProjectRegistered
126
+ for projectId
127
+ projectId = projectId
128
+ name = name
129
+ event ProjectRegistered
130
+ projectId ProjectId
131
+ name ProjectName
132
+ specification RegisteringAProject
133
+ when RegisterProject
134
+ projectId = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
135
+ name = "Screenplay"
136
+ then ProjectRegistered
137
+ projectId = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
138
+ name = "Screenplay"
139
+ specification RejectingAnEmptyProjectName
140
+ when RegisterProject
141
+ projectId = "3fa85f64-5717-4562-b3fc-2c963f66afa6"
142
+ name = ""
143
+ then error "Project name is required"
144
+ slice StateView ProjectLookup
145
+ readmodel ProjectSummary
146
+ projectId ProjectId
147
+ name ProjectName
148
+ query ProjectById => ProjectSummary?
149
+ by projectId ProjectId
150
+ projection ProjectSummaryProjection => ProjectSummary
151
+ from ProjectRegistered key projectId
152
+ name = name
153
+ ```
154
+
155
+ The same corpus ships as a NuGet package precisely so downstream tools verify
156
+ against these exact bytes. When in doubt about a construct's shape, start from
157
+ this file rather than from prose.
158
+
159
+ The repository's own showcase, `invoicing.play`, is a 981-line file that parses
160
+ with zero diagnostics and is pinned by 120 assertions. It is the
161
+ best available reference for the wider language — policies, personas,
162
+ authentication providers, composite `type` declarations, PDL joins, CDL captures,
163
+ screens and triggers.
164
+
165
+ ## The boundary that matters: parsed is not admitted
166
+
167
+ The parser accepts far more than anything can execute or render. Between the
168
+ syntax tree and any runtime sits the **executable semantic model (ESM v1)**, and
169
+ it fails closed.
170
+
171
+ ⚠️ `SemanticSliceKind` has exactly two members, `StateChange` and `StateView`.
172
+ An `Automation` or `Translate` slice is rejected outright with
173
+ `Slice '<name>' of type '<type>' is not admitted by ESM v1.` It parses, it
174
+ validates, and it binds to nothing.
175
+
176
+ Around forty further constructs bind to an `UnsupportedSemanticSyntax` error
177
+ rather than a weaker model — imports, policies, personas, triggers, `authorize`,
178
+ imperative `handler` bodies, code-backed `validate` rules, conditional
179
+ `produces … when`, query filters and performers, non-`by` queries, non-scalar
180
+ query results, `@pii`/`@sensitive` concepts, non-direct projection mappings,
181
+ parent keys and `sequence` among them.
182
+
183
+ The repository states the admitted set plainly: *"The minimum evaluator currently
184
+ admits the RegisterProject-style vertical: `not empty` validation, unconditional
185
+ event production, one affected read-model instance, optional snapshot lookup, and
186
+ exact ordered specification results. Unsupported reachable capabilities block
187
+ plan creation rather than producing a partial or stubbed execution."*
188
+
189
+ So: model the wider language when the `.play` file is the artifact you want —
190
+ documentation, review, a shared description of a system. Stay inside the
191
+ RegisterProject vertical when the model has to reach a runtime. Never assume a
192
+ construct works downstream because `screenplay` reported no diagnostics.
193
+
194
+ ## Editor support
195
+
196
+ The language service ships two ways: the Monaco package
197
+ `@cratis/screenplay-language`, and the VS Code extension `cratis.screenplay`
198
+ (marketplace id `cratis.screenplay`), which is the same service plus a `.play`
199
+ file icon.
200
+
201
+ ⚠️ Its keyword list has drifted from the parser. `theme`, `ui`, `form`,
202
+ `contribute`, `dialog` and `reducer` appear nowhere in the Monaco package,
203
+ although all six are real constructs the compiler accepts. They get no
204
+ highlighting and no completion. Absent highlighting is not evidence that a
205
+ construct is wrong — check `screenplay` instead.
206
+
207
+ The repository's TypeScript workspaces are **not** built, linted or tested by any
208
+ pull-request workflow; they run only inside the release job. Do not read a green
209
+ pull request as evidence the language service still compiles.
210
+
211
+ ## Verify
212
+
213
+ - `screenplay <model>` reports zero errors **and** zero warnings — an unknown
214
+ slice construct is only a warning and silently drops its block.
215
+ - Every name referenced across files resolves when the whole folder is compiled,
216
+ not just the file being edited.
217
+ - Concepts carrying personal data declare `@pii` with a reason, so the
218
+ classification travels with every use.
219
+ - If the model is meant to reach a runtime, every slice is `StateChange` or
220
+ `StateView` and stays inside the admitted vertical.
221
+ - Specifications express the intended behavior, including the rejection cases.
222
+
223
+ ## Route near misses
224
+
225
+ - Turning a model into an application, or the runtime sandbox:
226
+ `cratis-stage-rendering-and-sandbox`.
227
+ - Designing an event model as a diagram rather than as a `.play` file:
228
+ `cratis-event-model-diagram`.
229
+ - Writing the Chronicle projections, reactors or read models by hand: the
230
+ Chronicle guidance.
231
+ - Writing the Arc commands and queries by hand: `cratis-arc-command`.
@@ -0,0 +1,2 @@
1
+ Copyright (c) Cratis. All rights reserved.
2
+ Licensed under the MIT license. See LICENSE file in the project root for full license information.