@cratis/pi 7.38.3 → 7.39.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 (71) hide show
  1. package/package/corpus/agents/screenplay-modeler.md +1 -1
  2. package/package/corpus/agents/screenplay-reviewer.md +1 -1
  3. package/package/corpus/product-sync.json +3 -3
  4. package/package/corpus/skills/cratis-screenplay-automations-and-translations/SKILL.md +1 -1
  5. package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/automation-patterns.md +1 -1
  6. package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/public-events-and-direction.md +2 -5
  7. package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/todo-list-example.md +2 -5
  8. package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/translate-example.md +2 -5
  9. package/package/corpus/skills/cratis-screenplay-captures-and-reactions/SKILL.md +7 -11
  10. package/package/corpus/skills/cratis-screenplay-captures-and-reactions/references/complete-examples.md +6 -15
  11. package/package/corpus/skills/cratis-screenplay-captures-and-reactions/references/specifying-triggers-and-clock.md +1 -1
  12. package/package/corpus/skills/cratis-screenplay-command-surface/SKILL.md +14 -15
  13. package/package/corpus/skills/cratis-screenplay-command-surface/references/compliance.md +2 -2
  14. package/package/corpus/skills/cratis-screenplay-command-surface/references/enum-numbers.md +2 -5
  15. package/package/corpus/skills/cratis-screenplay-command-surface/references/inline-events.md +23 -1
  16. package/package/corpus/skills/cratis-screenplay-discovery/references/provenance.md +1 -1
  17. package/package/corpus/skills/cratis-screenplay-event-modeling/references/invoicing-example.md +3 -7
  18. package/package/corpus/skills/cratis-screenplay-event-modeling/references/nine-steps.md +7 -10
  19. package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/generate-notes.md +1 -1
  20. package/package/corpus/skills/cratis-screenplay-legacy-extraction/references/intent-example.md +3 -8
  21. package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +8 -3
  22. package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md +7 -5
  23. package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-loop.md +26 -6
  24. package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-tools.md +40 -0
  25. package/package/corpus/skills/cratis-screenplay-model-authoring/references/property-links.md +3 -1
  26. package/package/corpus/skills/cratis-screenplay-model-review/references/checklist.md +1 -1
  27. package/package/corpus/skills/cratis-screenplay-model-review/references/report-template.md +1 -1
  28. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/verdicts-and-modes.md +3 -2
  29. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/worked-example.md +2 -6
  30. package/package/corpus/skills/cratis-screenplay-projections/SKILL.md +2 -0
  31. package/package/corpus/skills/cratis-screenplay-read-surface/SKILL.md +26 -18
  32. package/package/corpus/skills/cratis-screenplay-read-surface/references/equality-filters.md +211 -0
  33. package/package/corpus/skills/cratis-screenplay-read-surface/references/screen-patterns.md +2 -5
  34. package/package/corpus/skills/cratis-screenplay-read-surface/verification.json +4 -0
  35. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/renderability-gate.md +1 -1
  36. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/worked-example.md +2 -5
  37. package/package/corpus/skills/cratis-screenplay-scenario-coverage/SKILL.md +4 -1
  38. package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/berth-reservations.md +8 -19
  39. package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/invoicing-dues-design.md +8 -20
  40. package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/invoicing-dues-example.md +9 -23
  41. package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/scenario-examples.md +9 -3
  42. package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/spec-forms-by-mode.md +3 -2
  43. package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/view-and-story-specs.md +2 -1
  44. package/package/corpus/skills/cratis-screenplay-slice-design/SKILL.md +7 -4
  45. package/package/corpus/skills/cratis-screenplay-slice-design/references/model-reasoning.md +2 -5
  46. package/package/corpus/skills/cratis-screenplay-slice-design/references/read-model-design.md +5 -10
  47. package/package/corpus/skills/cratis-screenplay-slice-design/references/worked-example.md +8 -19
  48. package/package/corpus/skills/cratis-screenplay-specifications/SKILL.md +17 -25
  49. package/package/corpus/skills/cratis-screenplay-specifications/references/actions-beyond-commands.md +2 -1
  50. package/package/corpus/skills/cratis-screenplay-specifications/references/persona-callers-and-cases.md +2 -5
  51. package/package/corpus/skills/cratis-screenplay-specifications/references/source-notes.md +10 -0
  52. package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/streams-example.md +10 -24
  53. package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +11 -6
  54. package/package/corpus/skills/cratis-screenplay-toolchain/references/automation-translate-example.md +4 -10
  55. package/package/corpus/skills/cratis-screenplay-toolchain/references/cheat-sheet-example.md +9 -20
  56. package/package/corpus/skills/cratis-screenplay-toolchain/references/doc-contradictions.md +2 -1
  57. package/package/corpus/skills/cratis-screenplay-toolchain/references/executable-example.md +3 -7
  58. package/package/corpus/skills/cratis-screenplay-toolchain/references/executable-subset.md +24 -8
  59. package/package/corpus/skills/cratis-screenplay-toolchain/references/pdl-example.md +27 -69
  60. package/package/corpus/skills/cratis-screenplay-toolchain/references/preferred-syntax.md +73 -0
  61. package/package/corpus/skills/cratis-screenplay-toolchain/references/renderable-subset.md +2 -2
  62. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract-exceptions.json +21 -0
  63. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +7 -1
  64. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
  65. package/package/corpus/skills/cratis-screenplay-toolchain/references/sources-and-streams.md +1 -1
  66. package/package/corpus/skills/cratis-screenplay-toolchain/references/traps.md +2 -2
  67. package/package/corpus/skills/cratis-screenplay-toolchain/references/verdicts.md +14 -2
  68. package/package/corpus/skills/cratis-screenplay-toolchain/references/versions.md +25 -5
  69. package/package/corpus/skills/cratis-screenplay-ui-composition/SKILL.md +7 -11
  70. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +2 -2
  71. package/package.json +1 -1
@@ -48,13 +48,9 @@ module Invoicing
48
48
  amount Decimal
49
49
  validate
50
50
  amount > 0 message "An invoice needs an amount above zero"
51
- produces InvoiceIssued
52
- for invoiceId
53
- member = member
54
- amount = amount
55
- event InvoiceIssued
56
- member MemberId
57
- amount Decimal
51
+ produces event InvoiceIssued
52
+ member MemberId = member
53
+ amount Decimal = amount
58
54
  constraint IssueOnce
59
55
  unique event InvoiceIssued
60
56
 
@@ -94,17 +90,12 @@ module Invoicing
94
90
  description "An invoice ends either settled or voided, never both: SettleOrVoid declares the exclusivity per invoice, enforced at append. Rules about the invoice's stored state are in invoicing-dues-design.md."
95
91
  command SettleInvoice
96
92
  invoiceId InvoiceId identifier
97
- produces InvoiceSettled
98
- for invoiceId
93
+ produces event InvoiceSettled
99
94
  command VoidInvoice
100
95
  invoiceId InvoiceId identifier
101
96
  reason String
102
- produces InvoiceVoided
103
- for invoiceId
104
- reason = reason
105
- event InvoiceSettled
106
- event InvoiceVoided
107
- reason String
97
+ produces event InvoiceVoided
98
+ reason String = reason
108
99
  constraint SettleOrVoid
109
100
  unique event InvoiceSettled
110
101
  unique event InvoiceVoided
@@ -170,9 +161,7 @@ module Invoicing
170
161
  description "Starts a payment attempt. PaymentRequested says only that we asked the gateway; it is NOT the outcome. The outcome arrives later as an external fact (slice PaymentGatewayResults). A request may be repeated after a decline, so there is deliberately no once-only constraint here."
171
162
  command RequestPayment
172
163
  invoiceId InvoiceId identifier
173
- produces PaymentRequested
174
- for invoiceId
175
- event PaymentRequested
164
+ produces event PaymentRequested
176
165
 
177
166
  specification RequestingPayment
178
167
  given caller
@@ -374,11 +363,8 @@ module Invoicing
374
363
  amount Decimal
375
364
  validate
376
365
  amount > 0 message "A top-up needs an amount above zero"
377
- produces CreditToppedUp
378
- for member
379
- amount = amount
380
- event CreditToppedUp
381
- amount Decimal
366
+ produces event CreditToppedUp
367
+ amount Decimal = amount
382
368
 
383
369
  specification ToppingUpCredit
384
370
  given caller
@@ -26,7 +26,7 @@ Screenplay specifications.
26
26
  |---|---|---|
27
27
  | Given | the facts that exist, each with its `for` source: `given InvoiceIssued` with `for "<invoice id>"` | "given an invoice" with no fact, no source |
28
28
  | When | one action with every input stated | an action with inputs left to defaults |
29
- | Then (success) | every production with payload and `for` | "an event is produced" |
29
+ | Then (success) | every expected event with payload and an explicit `for`, including events the command declares inline (the inline default applies to the `produces event` declaration, not to the expectation) | "an event is produced" |
30
30
  | Then (refusal) | `then error "Invoice already settled"`, the rule's own message | `then error` with no message, or `then error ""` |
31
31
  | Then (denial) | `then denied` | `then error` for a caller who is not allowed |
32
32
  | Reason | slice `documentation` says why the rule exists | the rule appears only in a spec title |
@@ -204,7 +204,13 @@ specification MemberCreditAccumulatesTopUps
204
204
  ```
205
205
 
206
206
  ## 6. Views: list contents and the empty list
207
- A list query asserts one `result` block per expected row (count and order are exact), and the
207
+ A list query asserts one `result` block per expected row: count is exact, but rows
208
+ match by read model and key regardless of order. The
208
209
  empty-list intent is a separate claim from a keyed row being absent (`view-and-story-specs.md`
209
210
  "Removal is a positive obligation"). The backing example proves the keyed forms only; whole-list
210
- queries are executable too (`ListOpenInvoices` in the backing example); only `filter`, `scoped to` and `performer` queries stay design-mode expectations recorded in the coverage matrix.
211
+ queries are executable too (`ListOpenInvoices` in the backing example); caller-supplied equality filters on collections bind as ESM v14 since 4.143.0.
212
+ Pass `by` and filters in query arguments; PLAY0468 reports unknown, repeated or
213
+ missing required arguments. Optional filters may be omitted, supplied null is refused.
214
+ Context-sourced/single-result filters, opaque `scoped to`/`performer`, and composite
215
+ `by` remain PLAY0268. Compiled coverage and renderer ceilings:
216
+ read-surface [equality filters](../../cratis-screenplay-read-surface/references/equality-filters.md).
@@ -17,7 +17,8 @@ Typed example/override forms: `cratis-screenplay-specifications`
17
17
  | `when append <Event>` | yes | yes | no |
18
18
  | `then readmodel` / `then query` (keyed `ById`, `arguments`, `result`) | yes | yes | yes |
19
19
  | `then query` on an unfiltered list or a list narrowed by one `by` key (observable too) | yes | yes since 4.105.0: no `arguments` for an unkeyed list, one `result` per row stating the read-model identifier, no `result` = empty list | observable lists render, a non-observable list is `STAGE-ESM-010` (Stage 4.73.1) |
20
- | `then query` on `filter`, `scoped to`, `performer` or `by ... from` queries | yes | no: PLAY0268 at binding | no |
20
+ | Collection query with caller-supplied equality `filter`, including observable | yes | yes since 4.143.0, ESM v14: same-named scalar property/type, AND with `by`; optional omission allowed, supplied null refused; PLAY0468 checks arguments; row order does not matter | no on Stage 4.73.1 (v10 ceiling) or latest checked 4.80.0 (v13 ceiling) |
21
+ | Context `from`, single-result filters, `scoped to`, `performer` or composite `by` | yes | PLAY0268: query context, collection-only filtering, opaque scope/implementation or multipart lookup are unadmitted | no |
21
22
  | `then no readmodel <RM> for "<key>"` | yes | yes | no |
22
23
  | `when query` -> `then result` / `then no result` | yes | yes | same as `then query`: only the query-only cases below |
23
24
  | `given clock` / `when clock`, `when trigger`, `given`/`when capture` | yes | yes (standalone and any current cratis; history: a cratis before 3.28.2 bundle no, see versions.md) | no |
@@ -40,7 +41,7 @@ cratis before 3.28.2 bundle is available). Mark each such spec in the coverage m
40
41
  `gap: <reason>` for the narrower modes so the verification and render phases know what will not execute.
41
42
 
42
43
  ## Executable mode
43
- - Every gated spec has `given caller`; every production and every given/expected event has `for`.
44
+ - Every gated spec has `given caller`; every given/expected event has `for`, and so does every plain `produces <Name>` (inline `produces event` omits it and defaults to the identifier).
44
45
  - Each read model has one key shared by its keyed `=> RM optional` queries; the identifier equals
45
46
  the projection's effective key (`cratis-screenplay-slice-design` `references/read-model-design.md`
46
47
  decision table). View specs assert through a keyed query.
@@ -119,7 +119,8 @@ Keep three claims apart:
119
119
  - one stored instance is absent: `then no readmodel`;
120
120
  - a query returns no rows: `then query` without `result`, or `when query` + `then no result`;
121
121
  - a list-valued property is empty: assert that property as `[]`.
122
- List-query results use repeated `result` blocks; count and order are exact. A keyed lookup does
122
+ List-query results use repeated `result` blocks; count is exact and rows match
123
+ by read model and instance key regardless of order. A keyed lookup does
123
124
  not prove an entire list is empty: assert the unkeyed list query itself (an unkeyed query takes no
124
125
  `arguments`; each `result` row states the read-model identifier, else PLAY0351). A snapshot list does not render
125
126
  (`STAGE-ESM-010`, Stage 4.73.1); an `observable` list does. Record that rendering limit.
@@ -73,7 +73,7 @@ report and slice `documentation`, and continue.
73
73
  `Save`, `Set`, `Change`, `Manage` + noun) means several decisions share a form: ask which
74
74
  separate reasons change these values, then split (`references/slicing.md` *Generic edits*).
75
75
  One input per thing the caller decides or supplies; `identifier` on the property naming the
76
- instance (omit only for allocation); `for <identifier>` on every `produces`. Trace each input
76
+ instance (omit only for allocation); inline `produces event <Name>` (no `for`) when the command owns the event; plain `produces <Name>` needs `for <identifier>`. Trace each input
77
77
  (`references/field-lineage.md`). A conditional input is a granularity question: two
78
78
  decisions are two commands; one decision with a dependent rule is an implication
79
79
  (`require a == false or b > c`). Never a boolean attestation input (`confirmsX == true`)
@@ -157,9 +157,12 @@ report and slice `documentation`, and continue.
157
157
  conventional `*Id` only with an admitted list query and no single-instance identifier).
158
158
  Queryless read models need explicit `key`.
159
159
  - At most one builder (projection or reducer) per read model (`PLAY0191`).
160
- - A query's `by` goes on its own body line. At 4.135.0, optional keyed and collection
161
- queries bind, including observable delivery; scalar `by` must be caller-supplied.
162
- Filtered, scoped, performer-backed and composite-block queries remain `PLAY0268`.
160
+ - A query's `by` goes on its own body line. Optional keyed and collection queries
161
+ bind, including observable delivery. Caller-supplied equality filters on collections
162
+ bind as ESM v14 since 4.143.0; `by` ANDs with supplied filters. `from`, single-result
163
+ filters, scope, performer and composite `by` remain PLAY0268 for their respective
164
+ context/cardinality/opaque/multipart gaps. Read read-surface
165
+ [equality filters](../cratis-screenplay-read-surface/references/equality-filters.md).
163
166
  - A `unique` constraint names a property the event declares directly; the constraint name is
164
167
  its identity (renaming starts an empty index).
165
168
  - Unauthorized is `then denied`; a broken rule is `then error`; authorization runs first.
@@ -37,11 +37,8 @@ module Billing
37
37
  ```
38
38
  recordId String identifier
39
39
  amount Int
40
- produces AmountRecorded
41
- for recordId
42
- amount = amount
43
- event AmountRecorded
44
- amount Int
40
+ produces event AmountRecorded
41
+ amount Int = amount
45
42
  specification RecordingAnAmount
46
43
  description "Witnesses that the agreed amount is retained"
47
44
  when RecordAmount amount = 10
@@ -136,8 +136,8 @@ it stays.
136
136
  | The person... | Design-mode query | Executable/renderable scope |
137
137
  |---|---|---|
138
138
  | opens one thing | `XById => RM optional` + `by xId XId` | binds at 4.135.0 with caller-supplied `by`; check target renderer |
139
- | scans a list | `ListX => RM[]` (optionally `observable`, `filter`) | collection and observable delivery bind at 4.135.0; filters remain PLAY0268; check target renderer |
140
- | sees their own rows | list with scope/`from` on the caller | scope and context-derived `by` remain PLAY0268; access rules are part of the view, enforced by the target |
139
+ | scans a list | `ListX => RM[]` (optionally `observable`, `filter`) | collection/observable delivery bind; caller-supplied equality filters bind since 4.143.0 as ESM v14; check target renderer |
140
+ | sees their own rows | list with scope/`from` on the caller | scope and context-derived `by`/`filter` remain PLAY0268 because portable scope/query context is unadmitted; caller-supplied filters are not access control |
141
141
  | sees a site-wide figure | singleton read model, `from <Event> key literal "global"` on every `from` (a projection-level `key` routes nothing, PLAY0381; example in Screenplay `projections/keys.md`, "Literal keys") | check the binding (V3, `cratis-screenplay-toolchain`) |
142
142
 
143
143
  - `observable` when people are expected to see others' or automation's changes while looking
@@ -231,11 +231,8 @@ module Berths
231
231
  command BookBerth
232
232
  bookingId BookingId identifier
233
233
  berth BerthName
234
- produces BerthBooked
235
- for bookingId
236
- berth = berth
237
- event BerthBooked
238
- berth BerthName
234
+ produces event BerthBooked
235
+ berth BerthName = berth
239
236
  specification BookingABerth
240
237
  given caller
241
238
  authenticated
@@ -250,9 +247,7 @@ module Berths
250
247
  description "A skipper cancels a booking they no longer need"
251
248
  command CancelBooking
252
249
  bookingId BookingId identifier
253
- produces BookingCancelled
254
- for bookingId
255
- event BookingCancelled
250
+ produces event BookingCancelled
256
251
 
257
252
  slice StateView OpenBookings
258
253
  description "The harbour office lists bookings that still hold a berth. BerthBooked sets berth: the booking enters the list. BookingCancelled removes it: a cancelled booking never shows."
@@ -62,13 +62,9 @@ module Lockers
62
62
  number LockerNumber
63
63
  volume Litres
64
64
  authorize IsAttendant
65
- produces LockerInstalled
66
- for lockerId
67
- number = number
68
- volume = volume
69
- event LockerInstalled
70
- number LockerNumber
71
- volume Litres
65
+ produces event LockerInstalled
66
+ number LockerNumber = number
67
+ volume Litres = volume
72
68
  constraint UniqueLockerNumber
73
69
  unique number on LockerInstalled
74
70
  message "That locker number is already installed"
@@ -141,13 +137,9 @@ module Lockers
141
137
  command RequestLocker
142
138
  requestId RequestId identifier
143
139
  preferredVolume Litres
144
- produces LockerRequested
145
- for requestId
146
- preferredVolume = preferredVolume
147
- requestedAt = $context.occurred
148
- event LockerRequested
149
- preferredVolume Litres
150
- requestedAt DateTime
140
+ produces event LockerRequested
141
+ preferredVolume Litres = preferredVolume
142
+ requestedAt DateTime = $context.occurred
151
143
  specification RequestingALocker
152
144
  given caller
153
145
  authenticated
@@ -180,11 +172,8 @@ module Lockers
180
172
  message "The locker is not free"
181
173
  require request.state == "waiting"
182
174
  message "The request is not waiting for a locker"
183
- produces LockerAssigned
184
- for lockerId
185
- requestId = requestId
186
- event LockerAssigned
187
- requestId RequestId
175
+ produces event LockerAssigned
176
+ requestId RequestId = requestId
188
177
  constraint OneRequestPerLocker
189
178
  unique event LockerAssigned
190
179
  message "The locker is already assigned"
@@ -6,23 +6,19 @@ license: MIT
6
6
 
7
7
  # Screenplay specifications
8
8
 
9
+ Read [preferred syntax](../cratis-screenplay-toolchain/references/preferred-syntax.md) when writing or reviewing `.play`.
10
+
9
11
  A `specification` is the acceptance criterion for a slice, written in the same
10
12
  document as the behavior it pins. Given/when/then, in business language, with
11
13
  concrete data.
12
14
 
13
15
  ## Locate the model
14
16
 
15
- Look first for the project's existing `.play` files: the folder holding them is the
16
- model. A new model goes under the repository's `Source/` or `src/` folder, else in a
17
- `Screenplay/` folder at the repository root; never under `.cratis/`, which holds
18
- configuration and the shared AI corpus only. This is the
19
- conventional home for consumer-owned `.play` source; do not invent another
20
- location or search the whole repository before checking it.
21
-
22
- `cratis ai install` manages `.cratis/ai/`, never model files. Never
23
- hand-copy Screenplay source between repositories. Keep Markdown that explains,
24
- questions or navigates the model in the repository's documentation; the `.play`
25
- source is the single flow model.
17
+ Look first for the project's existing `.play` files: their folder is the model.
18
+ For a new model, check `Source/` or `src/`, else use `Screenplay/` at the repository
19
+ root. Never put a model under `.cratis/` or hand-copy it between repositories.
20
+ `cratis ai install` manages `.cratis/ai/`, never model files. Keep explanatory
21
+ Markdown in project documentation; `.play` is the single flow model.
26
22
 
27
23
  ## Verified product sources
28
24
 
@@ -34,20 +30,15 @@ Specification syntax was checked against Screenplay v4.127.0 `specifications.md`
34
30
  older probes in the table remain historical. Pins: `cratis-screenplay-toolchain`
35
31
  `references/versions.md`. Compile commands check source, not reference execution.
36
32
  Use `screenplay test` or MCP `run-specifications` for V4; Stage/target tests are
37
- separate evidence. Unsupported/unbound is never a pass. Typed examples and
33
+ separate evidence. On Screenplay **4.145.0 and later**, propose → review →
34
+ `run-specifications` with `proposalId` → apply.
35
+ PLAY0285 (an impossible `then`) unbinds the whole model and blocks every spec.
36
+ Unsupported/unbound is never a pass. Typed examples and
38
37
  route assertions are in [examples and routes](references/examples-and-routes.md).
39
38
 
40
- Checked against the Screenplay repository at tag `v4.31.0` (commit `355dffb`):
41
- `Documentation/screenplay/{specifications,policies,constraints,readmodels,diagnostics}.md`.
42
- The original worked example had reference-runner evidence at that tag. The
43
- example below now uses inline events and canonical optionality; keep its current
44
- compile check distinct from that historical execution evidence.
45
-
46
- The specification actions beyond commands (`given clock`, `when clock`,
47
- `when trigger`, `given capture`, `when capture`, `when query`, `then result`,
48
- `then no result`) were checked against tag `v4.48.0` (commit `3baf4a4`):
49
- `Documentation/screenplay/{specifications,diagnostics}.md` and the proposed
50
- decision 0022. They do not exist before v4.48.0.
39
+ Read [source notes](references/source-notes.md) for the original v4.31.0
40
+ reference-runner evidence and v4.48.0 action syntax. Historical evidence is not
41
+ current execution evidence; run the changed examples at the standalone pin.
51
42
 
52
43
  ## Reference execution — what actually runs
53
44
 
@@ -171,8 +162,9 @@ the one-rejection rule (`PLAY0273`) are reported only when the model binds, so
171
162
  `when append`, a model before ESM v6 expects the appended fact itself; from
172
163
  ESM v6 the appended event is the action and `then` lists only what followed it.
173
164
  - **Read models and query rows:** only the asserted properties must match
174
- (subset); add `exactly` to require every property. Row count and order are
175
- always exact. A missing property does not match an asserted `null`.
165
+ (subset); add `exactly` to require every property. Query row count is exact;
166
+ collection rows match by read model/key regardless of order. Ordering within a
167
+ collection-valued property is unchanged. A missing property does not match an asserted `null`.
176
168
  - **Rejections:** `then error "<message>"` matches the message whatever the rule's
177
169
  validation severity; no form asserts severity. Bare `then error` never matches
178
170
  a denial. Quote a localized key: `then error "$strings.invoices.reasonRequired"`.
@@ -21,7 +21,8 @@ report the complete example in [reactions-and-cascades.md](reactions-and-cascade
21
21
 
22
22
  - **Screenplay 4.66.0 (ESM v6, from 4.61.0), standalone and `cratis` 3.28.2:** the clock, trigger
23
23
  and capture actions, and the `Automation` and `Translate` slices, reactions and
24
- captures they drive, bind. The reference runner executes them as library code, with
24
+ captures they drive, bind. The reference runner executes them through `screenplay test` or MCP
25
+ `run-specifications`, with
25
26
  the semantics in [Reactions and cascades](reactions-and-cascades.md).
26
27
  - **`cratis` before 3.28.2 (Screenplay 4.60.1, ESM v1-v5):**
27
28
  **`when query` executed. The clock, trigger and capture actions did not.** They parsed, printed and were
@@ -63,11 +63,8 @@ module Billing
63
63
  validate
64
64
  require amount > 0
65
65
  message "The amount must be positive"
66
- produces AmountRecorded
67
- for recordId
68
- amount = amount
69
- event AmountRecorded
70
- amount Int
66
+ produces event AmountRecorded
67
+ amount Int = amount
71
68
  specification RecordingAmounts
72
69
  description "An accountant can record a positive amount"
73
70
  parameter amount Int
@@ -3,6 +3,16 @@
3
3
 
4
4
  # Retained source notes
5
5
 
6
+ Original evidence: Screenplay v4.31.0 (`355dffb`),
7
+ `Documentation/screenplay/{specifications,policies,constraints,readmodels,diagnostics}.md`.
8
+ The original worked example had reference-runner evidence at that tag; inline
9
+ events and canonical optionality added later need their own current check.
10
+
11
+ Actions beyond commands (`given clock`, `when clock`, `when trigger`, `given capture`,
12
+ `when capture`, `when query`, `then result`, `then no result`) were checked at
13
+ v4.48.0 (`3baf4a4`), `Documentation/screenplay/{specifications,diagnostics}.md`
14
+ and proposed decision 0022. These actions do not exist before v4.48.0.
15
+
6
16
  | Package | Version | Purpose |
7
17
  | --- | --- | --- |
8
18
  | `Cratis.Screenplay` | `4.31.0` | Original parser, binder and reference execution evidence |
@@ -49,13 +49,9 @@ module Membership
49
49
  email Email
50
50
  name String
51
51
  authorize IsCoordinator
52
- produces MemberRegistered
53
- for memberId
54
- email = email
55
- name = name
56
- event MemberRegistered
57
- email Email
58
- name String
52
+ produces event MemberRegistered
53
+ email Email = email
54
+ name String = name
59
55
  constraint UniqueMemberEmail
60
56
  unique email on MemberRegistered
61
57
  ignore casing
@@ -102,11 +98,8 @@ module Courses
102
98
  command OpenCourse
103
99
  courseId CourseId identifier
104
100
  seats Int
105
- produces CourseOpened
106
- for courseId
107
- seats = seats
108
- event CourseOpened
109
- seats Int
101
+ produces event CourseOpened
102
+ seats Int = seats
110
103
  specification OpeningACourse
111
104
  given caller
112
105
  authenticated
@@ -176,13 +169,9 @@ module Courses
176
169
  validate
177
170
  require course.openSeats > 0
178
171
  message "The course is full" // NOT enforced: the view lags (PLAY0268 at binding)
179
- produces MemberEnrolled
180
- for enrolmentId
181
- courseId = courseId // cross-stream references are payload
182
- memberId = memberId
183
- event MemberEnrolled
184
- courseId CourseId
185
- memberId MemberId
172
+ produces event MemberEnrolled
173
+ courseId CourseId = courseId // cross-stream references are payload
174
+ memberId MemberId = memberId
186
175
  constraint OneActiveEnrolmentPerMemberAndCourse
187
176
  unique courseId, memberId on MemberEnrolled
188
177
  released by EnrolmentWithdrawn
@@ -276,11 +265,8 @@ module Courses
276
265
  validate
277
266
  require enrolment.courseId == courseId
278
267
  message "The enrolment does not belong to that course" // NOT enforced today
279
- produces EnrolmentWithdrawn
280
- for enrolmentId
281
- courseId = courseId
282
- event EnrolmentWithdrawn
283
- courseId CourseId
268
+ produces event EnrolmentWithdrawn
269
+ courseId CourseId = courseId
284
270
  constraint WithdrawOnce
285
271
  unique event EnrolmentWithdrawn
286
272
  specification WithdrawingAnEnrolment
@@ -6,6 +6,8 @@ license: MIT
6
6
 
7
7
  # Screenplay toolchain
8
8
 
9
+ Read [preferred syntax](references/preferred-syntax.md) when writing or reviewing `.play`.
10
+
9
11
  Which tool to run, what its result means, and what each tool can and cannot admit. This
10
12
  skill answers "does it compile, bind, render, and which compiler said so". The method
11
13
  (what to model, in which order, with which gates) lives in
@@ -22,7 +24,7 @@ files is one application: always check the folder, never one file of it.
22
24
 
23
25
  When choosing or upgrading a tool, or checking version-specific capabilities, read `references/versions.md`; it is the only version table, and other skills point to it. For exact diagnostic codes, MCP names/parameters and standalone CLI options, search the large published [references/screenplay-contract.json](references/screenplay-contract.json), pinned by [references/screenplay-contract.version](references/screenplay-contract.version), rather than loading it in full. Locate a diagnostic by its exact `code` (for example `"code": "PLAY0029"`), a tool by its exact `name` (for example `"name": "read-workspace"`), or a command in `cliCommands`; read the matched object for retirement, parameters or options. [references/screenplay-contract-exceptions.json](references/screenplay-contract-exceptions.json) records reasoned exceptions for historical statements; it does not change the current contract.
24
26
 
25
- The standalone pin is Screenplay 4.142.0.
27
+ The standalone pin is Screenplay 4.145.0.
26
28
  CLI and bundled package pins: `references/versions.md`. The single version table records
27
29
  released source evidence and historical probes separately; do not relabel an old
28
30
  render/browser result as a probe of the current packages.
@@ -31,7 +33,7 @@ Historical screens-release evidence: CLI 3.40.7 / Stage 4.51.1 / bundled
31
33
  Screenplay 4.114.0 opened the canonical corpus with `executableReady true`
32
34
  and the stdio transcript exposed 37 tools; standalone language 4.122.0 was
33
35
  recorded in that vector. These retained results are not a rerun on CLI 3.53.1
34
- or Screenplay 4.142.0.
36
+ or Screenplay 4.145.0.
35
37
 
36
38
  ## Ground rules
37
39
 
@@ -44,7 +46,7 @@ or Screenplay 4.142.0.
44
46
  3. **Pick the compiler deliberately and name it.** Standalone `screenplay` for
45
47
  current language checks and reference specifications; `cratis` for rendering
46
48
  and generation. Bundled and standalone versions differ (CLI 3.53.1 bundles 4.137.0;
47
- the standalone pin is 4.142.0). Report both tool and
49
+ the standalone pin is 4.145.0). Report both tool and
48
50
  version, rather than transferring a verdict across releases.
49
51
  4. **Know the mode** (design, executable, renderable) and write from its subset.
50
52
  5. **Capability is not correctness.** The tool you ran decides what is accepted today; the
@@ -91,7 +93,7 @@ server with a fixed root when the tool is older than 4.63.2 (the roots bug hit 4
91
93
  4.60.1 bundled by cratis before 3.28.2; 4.66.0 and later and cratis 3.28.2 are fine). A subagent without MCP reports "V2 not run: no MCP in this agent".
92
94
 
93
95
  One line per verdict, tool first, for example `V1 pass (screenplay 4.142.0, 3 files)` and
94
- `V3 blocked: PLAY0268 x3 (filter query, enum compliance)`. Name the source identity (commit plus the digest from
96
+ `V3 blocked: PLAY0268 x3 (context filter, enum compliance)`. Name the source identity (commit plus the digest from
95
97
  the source-identity helper (`cratis-screenplay-modeling-lifecycle` `references/verdicts-and-modes.md` "Source identity")) the verdict ran on; keep it apart from the MCP
96
98
  `modelRevision`, which is semantic and, without `.screenplay/identities.json`,
97
99
  changes with the root folder name.
@@ -101,7 +103,10 @@ changes with the root folder name.
101
103
  - **Design**: every construct family parses; use it to model first. Compiled shapes:
102
104
  `references/cheat-sheet.md`. Read [references/cheat-sheet-example.md](references/cheat-sheet-example.md) when you need one complete design-mode document with every construct family.
103
105
  - **Executable** (binds, V3): `references/executable-subset.md`. Optional keyed lookups, unfiltered
104
- lists and lists narrowed by one `by` key (observable too); `filter`, `scoped to`, `performer` and composite `by` do not bind; no handler (PLAY0268, with or without `implementation`/`hint`); `reads` and
106
+ lists and lists narrowed by one `by` key (observable too); caller-supplied equality
107
+ filters on collections bind as v14 since 4.143.0. Context `from`, single-result
108
+ filters, opaque `scoped to`/`performer` and composite `by` remain PLAY0268. Read
109
+ read-surface [equality filters](../cratis-screenplay-read-surface/references/equality-filters.md). No handler (PLAY0268, with or without `implementation`/`hint`); `reads` and
105
110
  `concurrency` do not bind (PLAY0271); concept compliance binds as v13 on standalone
106
111
  4.142.0 (since 4.141.0), not the pinned CLI (precise refusals: command-surface `references/compliance.md`);
107
112
  v6 constructs bind on Screenplay 4.61 or
@@ -168,7 +173,7 @@ All 44, with fixes: `references/traps.md`. Read `references/traps.md` when diagn
168
173
  Read [references/diagnostics.md](references/diagnostics.md) when a PLAY, STAGE or CLI-RENDER diagnostic needs its meaning and fix. Read [references/doc-contradictions.md](references/doc-contradictions.md) when the Screenplay documentation and the compiler disagree and you must decide which to trust.
169
174
 
170
175
  1. Clean V1 is not executable: only MCP (V2) or `render` binds.
171
- 2. Write `for <identifier>` on every `produces`.
176
+ 2. Prefer inline `produces event` for a command-owned event; plain command productions need explicit `for <identifier>`.
172
177
  3. PLAY0029 is a warning: validate with warnings as errors.
173
178
  4. Validate the folder; name the compiler (cli#244, cli#242).
174
179
  5. V1 warns on undeclared projection targets (`PLAY0514`) and removal/capture
@@ -17,11 +17,8 @@ module Membership
17
17
  slice StateChange Subscribe
18
18
  command Subscribe
19
19
  memberId MemberId identifier
20
- produces MemberSubscribed
21
- for memberId
22
- subscribedAt = $context.occurred
23
- event MemberSubscribed
24
- subscribedAt DateTime
20
+ produces event MemberSubscribed
21
+ subscribedAt DateTime = $context.occurred
25
22
  specification SubscribingAMember
26
23
  given clock "2026-10-05T09:00:00Z"
27
24
  when Subscribe
@@ -39,11 +36,8 @@ module Membership
39
36
  months Int
40
37
  validate
41
38
  months > 0 message "Renew for at least one month"
42
- produces MembershipRenewed
43
- for memberId
44
- months = months
45
- event MembershipRenewed
46
- months Int
39
+ produces event MembershipRenewed
40
+ months Int = months
47
41
  specification RejectingAZeroRenewal
48
42
  when Renew
49
43
  memberId = "5b6c1f7e-2d3a-4e8b-9c0d-1a2b3c4d5e6f"
@@ -2,7 +2,7 @@
2
2
 
3
3
  One complete document with every construct family. Design mode, not executable. Prose rules: [cheat-sheet.md](cheat-sheet.md); tool versions: [versions.md](versions.md); what binds: [executable-subset.md](executable-subset.md).
4
4
 
5
- Binding blockers: unquoted import, `reads` (PLAY0271), `starts with`, a `filter` query (lists and observable lists bind). Executable shapes are in the sibling examples. The bare-marker fence compiles warning-free on standalone 4.127.0; the earlier legacy-marker version compiled on 4.66.0 and CLI 3.28.2. `RecordingAPayment` lists the `InvoiceClosed` cascade, which the cratis 3.27.1 bundle (4.60.1) rejected with a false PLAY0285 ("outcome cannot be produced by 'RecordPayment'"); 3.28.2 accepts it.
5
+ Binding blockers: unquoted import, `reads` (PLAY0271), `starts with`. Caller-supplied collection equality filters bind as ESM v14 since 4.143.0; context/single-result filters remain blocked. Executable shapes are in the sibling examples. The bare-marker fence compiles warning-free on standalone 4.127.0; the earlier legacy-marker version compiled on 4.66.0 and CLI 3.28.2. `RecordingAPayment` lists the `InvoiceClosed` cascade, which the cratis 3.27.1 bundle (4.60.1) rejected with a false PLAY0285 ("outcome cannot be produced by 'RecordPayment'"); 3.28.2 accepts it.
6
6
 
7
7
  Concept compliance itself binds as ESM v13 on standalone 4.142.0 (first released
8
8
  in 4.141.0); reasons remain report-only PLAY0270. CLI 3.53.1 bundles Screenplay
@@ -135,9 +135,7 @@ module Billing
135
135
  command CancelInvoice
136
136
  invoiceId InvoiceId identifier
137
137
  authorize CanManage
138
- produces InvoiceCancelled
139
- for invoiceId
140
- event InvoiceCancelled
138
+ produces event InvoiceCancelled
141
139
  specification CancellingAnInvoice
142
140
  given caller
143
141
  authenticated
@@ -161,7 +159,7 @@ module Billing
161
159
  number String
162
160
  query InvoiceById => InvoiceRow optional // keyed optional lookup (unfiltered lists bind too)
163
161
  by invoiceId InvoiceId
164
- query ListInvoices => observable InvoiceRow[] // design mode only: the `filter` below is PLAY0268 at binding
162
+ query ListInvoices => observable InvoiceRow[] // caller-supplied equality filter: ESM v14 since 4.143.0
165
163
  filter number String optional
166
164
  projection InvoiceList => InvoiceRow
167
165
  from InvoiceRegistered
@@ -214,13 +212,9 @@ module Billing
214
212
  paymentId PaymentId identifier
215
213
  invoiceId InvoiceId
216
214
  amount Decimal
217
- produces PaymentRecorded
218
- for paymentId
219
- invoiceId = invoiceId // cross-stream reference is payload
220
- amount = amount
221
- event PaymentRecorded
222
- invoiceId InvoiceId
223
- amount Decimal
215
+ produces event PaymentRecorded
216
+ invoiceId InvoiceId = invoiceId // cross-stream reference is payload
217
+ amount Decimal = amount
224
218
  specification RecordingAPayment
225
219
  given caller
226
220
  authenticated
@@ -242,9 +236,7 @@ module Housekeeping // no gates: this reaction
242
236
  slice StateChange CloseInvoice
243
237
  command CloseInvoice
244
238
  invoiceId InvoiceId identifier
245
- produces InvoiceClosed
246
- for invoiceId
247
- event InvoiceClosed
239
+ produces event InvoiceClosed
248
240
  specification ClosingAnInvoice
249
241
  when CloseInvoice
250
242
  invoiceId = "9c858901-8a57-4791-81fe-4c455b099bc9"
@@ -254,11 +246,8 @@ module Housekeeping // no gates: this reaction
254
246
  slice StateChange MarkOverdue
255
247
  command MarkOverdue
256
248
  invoiceId InvoiceId identifier
257
- produces InvoiceMarkedOverdue
258
- for invoiceId
259
- overdueAt = $context.occurred
260
- event InvoiceMarkedOverdue
261
- overdueAt DateTime
249
+ produces event InvoiceMarkedOverdue
250
+ overdueAt DateTime = $context.occurred
262
251
  // No command spec here: under ESM v6 it must list the ReminderSent cascade. The file is
263
252
  // RecordingAPayment's cascade got a false PLAY0285 on cratis before 3.28.2; 3.28.2 accepts it.
264
253
  // The cascade form: automation-translate-example.md (SubscribingAMember).
@@ -41,13 +41,14 @@ read from source or documentation.
41
41
  | 7 | interaction examples | `D/interactions.md:33` uses `button` | PLAY0103; `on double click` on `table` compiles though not in the EBNF |
42
42
  | 8 | where screens live | `D/screens.md:3` "inside `StateView` slices" | samples also put dialogs in `StateChange` |
43
43
  | 9 | where reducers live | `D/slices.md:74` and `D/glossary.md:21` call a reducer an `Automation` thing vs `D/slices.md:87` (`StateView`) | `StateView` (samples) |
44
+ | Query rows | Older specification guidance promises authored result-row order | Screenplay v4.145.0 `specifications.md:307-319,426`: compare collections without regard to order, by read-model/key and exact count. Event ordering and collection-valued property ordering are separate |
44
45
  | 10 | `then` after `when append` | `D/specifications.md:67` "must match exactly the appended fact" vs `:232` and `:282` (v6: what followed it) | v6 text on the standalone tool; before v6, `:67` |
45
46
  | 11 | constraint collision across sources | `D/constraints.md:118` "cannot yet show" vs `D/specifications.md:77` (`for` lets a spec claim a value on another source) | `specifications.md` (newer) |
46
47
  | 12 | capture backtick `when` | opaque (`D/captures/grammar.md:321`) vs small portable grammar (`D/captures.md:94`) | both: storage versus binder |
47
48
  | 13 | clock boundary | resolved: an occurrence fires if due **after** the given clock and **at or before** the when clock (`D/specifications.md:282-284`; `Semantics/Execution/SemanticClock.cs`, `at > from && at <= to`). Given equal to when fires nothing, so the Screenplay sample `Samples/Commerce/Fulfillment/Shipping/ReleasePickingWave.play:20-22` (06:00 to 06:00, expecting `PickingWaveReleased`) is wrong | the code; never write given equal to when. A Screenplay issue for the sample is a candidate |
48
49
  | 14 | `$causedBy` | not evaluable (#4119), not ESM | showcase only |
49
50
  | 15 | literal keys | `key literal "global"` vs `key "global"` | both compile; prefer `literal` |
50
- | 16 | query admission | `D/queries.md:74` now says live, filtered, scoped and performer queries are not admitted | the binder also rejects collection queries and a missing `by`, which stay undocumented [probed] |
51
+ | 16 | query admission | Older `D/queries.md:74` and historical binder probes rejected live/collection/filter queries | Current v4.145.0 `queries.md` admits collection/observable queries and caller-supplied equality filters (ESM v14 since 4.143.0). Context `from`, single-result filters, opaque scope/performer and composite `by` remain PLAY0268 with specific reasons; source/refusal details: read-surface `references/equality-filters.md` |
51
52
  | 17 | `@pii` | `D/concepts.md:3,33` "Chronicle applies `@pii` and `@sensitive` rules automatically" | the historical binder rejected any compliance attribute (PLAY0268); standalone 4.142.0 admits concept compliance as v13 (since 4.141.0) with narrower enum/identity/subject-lineage refusals (command-surface `references/compliance.md`). Keep protection; the pinned CLI still reports V3 blocked. `@sensitive` means an operational secret (`[Encrypted]` + `[NotAudited]`; Screenplay decision 0034); the historically probed tools (standalone 4.68.0, bundle 4.66.0) do **not** report `PLAY0515` (`@pii` from 4.69.1, `@sensitive` from 4.84.1): there only Chronicle catches either attribute on an event-source identifier, at build time of the generated code (CHR0034 for `@pii`, CHR0052 for `@sensitive`) |
52
53
  | 18 | read-model identity | `D/readmodels.md:32` "from the keyed query" | at 4.135.0 explicit `key` wins; else single-instance queries agree on one read-model `by` property; only with none and an admitted list query does conventional `*Id` inference run (collection-key exclusions: read-surface `references/named-keys.md`). Queryless views need explicit `key` |
53
54
  | 19 | `$strings` constraint messages | `D/constraints.md:31` vs `D/internationalization.md:55` | the compiler keeps the key; realization localizes |