@cratis/pi 7.38.2 → 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
@@ -6,6 +6,7 @@
6
6
  ## Contents
7
7
 
8
8
  - Tool groups
9
+ - Run reference specifications
9
10
  - Whole-source authoring
10
11
  - Diagnostic repairs and refactorings
11
12
  - Syntax-only constructs through the MCP
@@ -53,6 +54,7 @@ the [MCP loop](mcp-loop.md).
53
54
  | Visual (MCP-Apps hosts only) | `visualize-model` | Draw the board for the model or a `proposalId`; its `sketch` argument previews a what-if of whole documents. Reads only |
54
55
  | Planning | `propose-source`, `propose-ast`, `propose-rename`, `propose-move`, `propose-repair`, `propose-property`, `propose-number-enum-members`, `propose-extract-inline-event`, `propose`, `recommend-layout`, `expand-layout` | Produce validated, reviewable candidates without writing them |
55
56
  | Review | `read-proposal`, `discard-proposal` | Inspect exact changes, dropped comments and proposed attachments, or abandon a connection-local proposal |
57
+ | Reference execution | `run-specifications` | Run the workspace or, from 4.145.0, a retained proposal candidate read-only; not attached code or target tests |
56
58
  | Effects | `apply`, `recover-workspace` | Apply an accepted plan or explicitly recover an interrupted write |
57
59
 
58
60
  `propose-move` moves a slice to another feature, or a feature to another module or feature, keeping identities: copy the `target` and `newParent` `{kind, parts}` addresses from the identity inventory and pass `expectedRevision` and `expectedCatalogRevision`. It repairs qualified references and `depends on`, and refuses identity loss, capture, collisions, descendant destinations, inherited authorization or screen-interaction changes and glob imports; review `moveReport` (`identityMigrations`, `referenceRepairs`, `fragmentsMoved`). It does not rename events, change pins or move files, and declaration moves between slices are deferred. Use it for legacy re-slicing instead of retyping declarations.
@@ -71,6 +73,44 @@ Exact arguments, AutoMap and fixture rules: [property evidence and planning](pro
71
73
  source-only authoring proposal may be accepted while `executableReady` is false.
72
74
  That is not permission to claim the application runs.
73
75
 
76
+ ## Run reference specifications
77
+
78
+ `run-specifications` runs the deterministic in-memory reference evaluator, not
79
+ Arc/Chronicle, external services or opaque code. It writes no files. Parameters:
80
+ `specification` (exact dotted address or semantic id), `scope` (module/feature/slice
81
+ prefix), `offset` (default 0), `limit` (1–200, default 50), and
82
+ `expectedSourceRevision` (required for `offset > 0`). Both selectors mean
83
+ intersection; no match refuses `UnknownSpecificationSelection`, never an empty pass.
84
+ A table address selects all its cases. Drift refuses `StaleSourceRevision`.
85
+
86
+ **Screenplay 4.145.0 and later** also accepts `proposalId`: propose → review
87
+ `read-proposal` (including `view: "semantic-diff"`) → run the retained candidate →
88
+ `apply`. Static expectation comparison is not execution. The candidate retains
89
+ identities and planned renames; running does not apply, refresh from disk or discard
90
+ it. A changed base does not prevent candidate execution but still blocks stale
91
+ apply. Applied/discarded proposals and workspace reopen give `UnknownProposal`.
92
+ Continue with the same `proposalId` and returned `sourceRevision` as
93
+ `expectedSourceRevision`. Without `proposalId`, the tool runs the open workspace
94
+ or reads the root with its persisted identities.
95
+
96
+ The response includes `sourceRevision`, `outcome`, discovered/selected/executed/
97
+ passed/failed/unsupported counts, `executableDiagnostics`, `planIssues`, and `page`
98
+ (plus `proposalId` for candidate runs). Counts cover the entire selection, not one
99
+ page; unsupported cases are not executed. Outcomes are `passed`, `failed`,
100
+ `unsupported`, or overall `unbound`; only `passed` has `isError: false`. An unbound
101
+ model has `page: null`; an impossible `then` (PLAY0285) prevents every spec running.
102
+ Opaque capabilities report unsupported with a capability and reason, not a pass.
103
+ Zero discovered specifications is not coverage evidence.
104
+
105
+ Outside MCP, use `screenplay test <root> [--filter <exact-address>] [--format text|json]`:
106
+ exit 0 passed, 1 failed, 2 invalid arguments/input/selection or IO, 3 unsupported
107
+ or unbound. `--no-color` is rejected (exit 2); there is no proposal or paging option.
108
+ A folder is one application; a file selects only itself and its imports.
109
+
110
+ Sources: Screenplay v4.145.0 `Source/DotNET/Screenplay.Mcp/McpWorkspaces.Specifications.cs:12-79`,
111
+ `McpSpecificationExecution.cs:20-49,106-111`, `Source/DotNET/Tool/ModelTest.cs:14-108`,
112
+ `Documentation/screenplay/mcp/authoring-tools.md:500-535` and `tool.md:120-142`.
113
+
74
114
  ## Whole-source authoring
75
115
 
76
116
  `propose-source` takes `expectedRevision`, `expectedCatalogRevision`, explicit
@@ -191,7 +191,9 @@ For a required addition with existing fixtures/assertions, supply `fixtureValue`
191
191
  as **literal source text**, for example `"\"normal\""` for a String value. The
192
192
  planner adds it to existing specification steps, examples and query results;
193
193
  `optional: true` adds no fixture values. A literal is not proof of behavior:
194
- inspect the assertions and run `run-specifications` on the retained proposal.
194
+ inspect the assertions and, on Screenplay **4.145.0 and later**, run
195
+ `run-specifications` with `proposalId` on the retained candidate before `apply`.
196
+ See [reference execution](mcp-tools.md#run-reference-specifications).
195
197
 
196
198
  | Refusal | Response |
197
199
  | --- | --- |
@@ -180,7 +180,7 @@ Nebulit (`provenance.md`).
180
180
  | Id | Check | Evidence | Tier |
181
181
  |---|---|---|---|
182
182
  | H1 | Query shape follows the business view (list where people scan; lookup where they open one) | queries, screens | D |
183
- | H2 | Executable scope: a keyed `=> RM optional` query with `by` named like a read-model property, or an unfiltered / one-`by` list (`=> RM[]`, observable too; `filter`, `scoped to`, `performer` do not bind), and the projection routes by the same identity (a `from` key or an event property, or `$eventSourceId`; `$eventSourceId` is not mandated). Renderable scope only: Stage-specific identity limits (Stage 4.73.1 renders a keyed optional snapshot lookup and observable collection queries; any other query is `STAGE-ESM-010`) | queries, projections | C (executable); C (render admission, renderable only) |
183
+ | H2 | Executable scope: a keyed `=> RM optional` query with `by` named like a read-model property, or a collection (`=> RM[]`, observable too) with caller-supplied `by` and/or equality filters (ESM v14 since 4.143.0). `from`, single-result filters, opaque `scoped to`/`performer` and composite `by` remain PLAY0268. The projection routes by the same identity (a `from` key or an event property, or `$eventSourceId`; `$eventSourceId` is not mandated). Filtered models: Stage 4.73.1 (through v10) and latest checked 4.80.0 (through v13) do not admit v14. Renderable scope only: Stage-specific identity limits (Stage 4.73.1 renders a keyed optional snapshot lookup and observable collection queries; any other query is `STAGE-ESM-010`) | queries, projections | C (executable); C (render admission, renderable only) |
184
184
  | H3 | One builder per read model; projection `key` only on `from` | projections | C |
185
185
  | H4 | Screens show one actor's point of view; a form submits one command | screens, forms | D |
186
186
 
@@ -35,7 +35,7 @@ Independence: independent (fresh context, different model family from every auth
35
35
  V1 authorable: <pass (tool, version, N files) | fail: PLAYnnnn at file:line>
36
36
  V2 executable diagnostics: <clean | n diagnostics: codes | not run: reason>
37
37
  V3 binding-ready: <ready | blocked: codes | not run: reason>
38
- V4 reference specs run: <route, expected vs discovered, passed/failed/unsupported/cancelled | not run: no route>
38
+ V4 reference specs run: <route, expected vs discovered, passed/failed/unsupported; overall unbound | not run: no route>
39
39
  V5 rendered, target-verified: admission <...>; publication <...>; build <...>; tests <...> (each "not run: reason" when absent)
40
40
  Gaps: <capability gaps and tool-version gaps, e.g. a false PLAY0285 on cascades from a cratis before 3.28.2 bundle>
41
41
 
@@ -21,13 +21,14 @@ defines what each result means and how to report it. Do not copy version facts h
21
21
  | V1 authorable | folder-mode validate with warnings as errors. The standalone compiler includes imports in folder and file mode; `cratis` CLI 3.28.2 file validation ignored imports (cli#244, probed); since 3.28.4 (PR #258) it compiles the file with its imports, but folder mode stays the recommended cross-tool check | semantics, binding, specs |
22
22
  | V2 executable diagnostics | the executable model reports no diagnostics for the scope | that any spec runs or passes |
23
23
  | V3 binding-ready | the model binds to the executable semantic model | that any spec runs or passes |
24
- | V4 reference specs run | the reference execution route only: engine, and per `.play` specification passed / failed / unsupported / cancelled | rendering, generated code, build or target tests |
24
+ | V4 reference specs run | the reference execution route only: engine, and per `.play` specification passed / failed / unsupported (overall model may be unbound) | rendering, generated code, build or target tests |
25
25
  | V5 delivery and target-verified | admission, publication, Debug build, Debug tests of generated specifications, each reported separately | customization conformance, faithful UI |
26
26
 
27
27
  Results are independent: V3 can be ready while V1 has warnings; V5 build can pass with V4 not
28
28
  run. Never infer one from another. Spec outcomes for V4 come only from a reference execution
29
29
  route. Written or bound specifications are never "specs pass", and rendered Debug tests are
30
- V5 evidence, not V4.
30
+ V5 evidence, not V4. A read-only `run-specifications` proposal run (Screenplay
31
+ 4.145.0+) is V4 evidence for that retained candidate only, not the disk model.
31
32
 
32
33
  V4 rules:
33
34
  - Report per `.play` specification (expected = specifications in the accepted scope; discovered =
@@ -34,12 +34,8 @@ module Berths
34
34
  command AllocateBerth
35
35
  berthId BerthId identifier
36
36
  boat BoatName
37
- produces BerthAllocated
38
- for berthId
39
- boat = boat
40
-
41
- event BerthAllocated
42
- boat BoatName
37
+ produces event BerthAllocated
38
+ boat BoatName = boat
43
39
 
44
40
  constraint OneBerthPerBoat
45
41
  unique boat on BerthAllocated
@@ -6,6 +6,8 @@ license: MIT
6
6
 
7
7
  # Projections — the Screenplay PDL
8
8
 
9
+ Read [preferred syntax](../cratis-screenplay-toolchain/references/preferred-syntax.md) when writing or reviewing `.play`.
10
+
9
11
  A `projection` folds events into a read model. Its body is the **Projection
10
12
  Declaration Language**, an embedded sub-grammar with its own parser — not free
11
13
  text. This is half of every event model, and the half most easily got wrong.
@@ -6,6 +6,8 @@ license: MIT
6
6
 
7
7
  # The Screenplay read surface
8
8
 
9
+ Read [preferred syntax](../cratis-screenplay-toolchain/references/preferred-syntax.md) when writing or reviewing `.play`.
10
+
9
11
  What the system can be asked, and what a user sees. The `readmodel` declares the
10
12
  shape, the `query` is the entry point, and the `screen` renders it.
11
13
 
@@ -32,7 +34,9 @@ source is the single flow model.
32
34
  | `Cratis.Screenplay` | `4.66.0` (`c89198b`) | Which query shapes bind and render (`Semantics/`, `Documentation/screenplay/queries.md`) |
33
35
 
34
36
  The update follows `types.md`, `queries.md` and `vscode.md` at that main commit
35
- (after v4.52.0). Compilation does not establish query execution.
37
+ (after v4.52.0). Compilation does not establish query execution. Current equality-filter evidence:
38
+ Screenplay v4.145.0 (admitted in 4.143.0, decision 0069); read
39
+ [the verified sources and compiled example](references/equality-filters.md).
36
40
 
37
41
  Checked against the Screenplay repository at tag `v4.31.0` (commit `355dffb`):
38
42
  `Documentation/screenplay/{queries,readmodels,screens,policies,specifications,interactions}.md`
@@ -82,12 +86,12 @@ query ListInvoices => InvoiceListReadModel[]
82
86
  authorize IsAuthenticated
83
87
  ```
84
88
 
85
- **Name the compiler and renderer.** At Screenplay 4.135.0, optional keyed queries
86
- and collection queries bind, including `observable`, with caller-supplied `by`
87
- or no `by` on a collection. Filters, scope, performer and context-derived `by`
88
- remain PLAY0268. The old Stage 4.24.0 list refusal is not a current compiler
89
- limitation; check the target renderer's admission separately. Keep a list the
90
- domain needs rather than inventing a keyed query solely to infer identity.
89
+ **Name the compiler and renderer.** Optional keyed queries and collection queries
90
+ bind, including `observable`. Since Screenplay 4.143.0, caller-supplied equality
91
+ filters on collections bind as ESM v14; `from`, single-result filters, `scoped to`,
92
+ `performer` and composite `by` remain PLAY0268, each with its reason. Read
93
+ [equality filters](references/equality-filters.md) for the admitted shape, a complete
94
+ compiled specification model and the Stage/CLI ceilings. Keep a list the domain needs.
91
95
 
92
96
  Return-type forms after `=>`: `ReadModel`, `ReadModel optional`, `ReadModel[]`,
93
97
  each optionally prefixed `observable`. `ReadModel[] optional` permits an absent
@@ -104,7 +108,7 @@ scalar type named `observable`. It is the canonical exception and has no
104
108
  | Clause | Meaning |
105
109
  | --- | --- |
106
110
  | `by` | the **identifying** parameter — the query returns the instance it identifies |
107
- | `filter` | an optional parameter narrowing the result set; usually typed `optional` |
111
+ | `filter` | a caller-supplied equality parameter narrowing the result set; required unless typed `optional` |
108
112
  | `from <source>` | fills the parameter **from the context** instead of from the caller |
109
113
 
110
114
  ⚠️ **Anything the caller must not be able to choose — the tenant, the caller's own
@@ -151,7 +155,7 @@ most reads are. The marker qualifies only *how* the result arrives, so it compos
151
155
  with `[]`, `optional`, `by`, `filter`, `authorize` and a `performer`, and a screen binds
152
156
  to a live query exactly as it binds to a one-shot one. The delivery itself is the
153
157
  target runtime's: the executable model binds `observable` queries (delivery `live`) that
154
- have no `filter`, `scoped to` or `performer`.
158
+ have no opaque scope/performer and, when filtered, only admitted caller-supplied equality filters (ESM v14).
155
159
 
156
160
  Paging, sorting and change-set delivery for queries are accepted as Screenplay
157
161
  decision 0010 but **not in the language yet**. Do not invent syntax for them.
@@ -174,15 +178,19 @@ on the header line is a declaration error, not a shorthand.
174
178
 
175
179
  ### What runs
176
180
 
177
- In the Screenplay 4.139.0 executable model (admitted since 4.105.0) these bind: a keyed
178
- `=> <ReadModel> optional` with one caller-supplied `by` argument, an unfiltered list
179
- `=> <ReadModel>[]`, a list narrowed by one caller-supplied `by` key, and the `observable`
180
- form of each. `filter`, `scoped to`, `performer`, `by ... from` and composite `by` report
181
- `PLAY0268`, as does a single non-optional `=> <ReadModel>`. Its `authorize` gate runs before
182
- the lookup, ANDed with module and feature gates; a denied caller gets
183
- `Unauthorized`. Pin that with a `given caller` fixture and `then denied` (see
184
- `cratis-screenplay-specifications`). Model `filter`, `scoped to` and `performer` shapes when the
185
- application needs them; just do not claim the reference runner exercised them.
181
+ These bind on standalone 4.145.0: keyed `=> RM optional`, unfiltered collections,
182
+ collections narrowed by caller-supplied `by`, and their `observable` forms.
183
+ Caller-supplied equality filters on collections select ESM v14: same-named scalar
184
+ primitive/concept/enum properties, same nominal type ignoring optionality, no
185
+ `from`, distinct names not repeating `by`. `by` and supplied filters AND together;
186
+ optional filters may be omitted, but supplied null is refused. PLAY0268 still
187
+ rejects context sources, single-result filters, opaque scope/performer and
188
+ composite `by` (#599); a single non-optional result remains unadmitted too.
189
+ Authorization runs before lookup, ANDed with module/feature gates; policies see
190
+ only `by`, never filters. Pin denial with `given caller` and `then denied`.
191
+ Specs pass `by` and filters by name (PLAY0468 for unknown, repeated or missing
192
+ required arguments); collection results match by read model/key regardless of
193
+ row order. Details and renderer limits: [equality filters](references/equality-filters.md).
186
194
 
187
195
  ## `screen` — three levels
188
196
 
@@ -0,0 +1,211 @@
1
+ <!-- Copyright (c) Cratis. All rights reserved. -->
2
+ <!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
3
+
4
+ # Caller-supplied equality query filters (ESM v14)
5
+
6
+ ## Contents
7
+
8
+ - Refusals kept intact
9
+ - Specifications that run
10
+ - Argument diagnostics
11
+ - Rendering is a different verdict
12
+ - Verified product sources
13
+
14
+ Screenplay **4.143.0 and later**, including the standalone **4.145.0** pin,
15
+ binds `filter <name> <Type> [optional]` on a collection query when:
16
+
17
+ - there is no `from` source;
18
+ - the read model has a same-named non-collection primitive, concept or enum property;
19
+ - the filter has the same nominal type as that property, ignoring optionality;
20
+ - filter names are distinct and none repeats the `by` name.
21
+
22
+ Each supplied value matches that property by equality. `by` and all supplied
23
+ filters combine with AND; a row missing the property does not match. A required
24
+ filter must be supplied. An optional filter may be omitted, in which case it
25
+ adds no restriction. **Supplied `null` is refused**, even for an optional filter;
26
+ it is not omission or a request to match null properties.
27
+
28
+ Filters select ESM v14. Models without filters retain their bytes, version and
29
+ revision. `observable` collections may use filters too, but the reference runner
30
+ answers a snapshot; change sets, paging, sorting and non-equality predicates
31
+ are not admitted by this increment. Query policies still see only `by`, not filters.
32
+ A caller-supplied filter is not a trusted tenant or authorization boundary.
33
+
34
+ ## Refusals kept intact
35
+
36
+ Each binding refusal reports PLAY0268 with its query/filter and reason:
37
+
38
+ | Shape | Why it remains unadmitted |
39
+ | --- | --- |
40
+ | `filter ... from ...` or context-derived `by` | Typed query context sources are not admitted; a caller argument must not stand in for trusted context |
41
+ | Filter on a single-result query | Equality filters are admitted only for collections |
42
+ | `scoped to` | The named scope has no portable executable scope contract |
43
+ | `performer` | Query implementation code is opaque, not a portable declarative lookup |
44
+ | Composite `by` block | Multipart lookup admission remains deferred (Screenplay#599) |
45
+ | Filter on a collection/composite property, missing property or mismatched nominal type | The v14 filter contract requires a same-named scalar primitive, concept or enum property with the same type |
46
+
47
+ Keep those intents when the domain needs them; never drop a context source or
48
+ scope to make a query bind.
49
+
50
+ ## Specifications that run
51
+
52
+ Both `then query … arguments` and `when query` name `by` and filter arguments.
53
+ Unknown, repeated or missing required arguments report **PLAY0468** in either
54
+ form. The query, its key and supplied filter values identify a call, so one
55
+ specification may assert two calls with different filters.
56
+
57
+ Collection results compare **without regard to row order**, matched by read
58
+ model and instance key. Row count is exact; asserted properties use subset
59
+ matching unless `exactly` is requested. This does not relax ordering of event
60
+ expectations or collection-valued properties inside one row.
61
+
62
+ This complete model checks omission, supplied filters, AND with `by`, both query
63
+ specification forms and reversed result rows:
64
+
65
+ ```screenplay
66
+ concept InvoiceId : String
67
+ concept CustomerId : String
68
+ concept InvoiceStatus : Enum
69
+ issued
70
+ paid
71
+ module Invoicing
72
+ feature Overview
73
+ slice StateView Invoices
74
+ readmodel InvoiceRow
75
+ invoiceId InvoiceId key
76
+ customerId CustomerId
77
+ status InvoiceStatus
78
+ query ListInvoices => InvoiceRow[]
79
+ filter status InvoiceStatus
80
+ filter customerId CustomerId optional
81
+ query InvoicesById => observable InvoiceRow[]
82
+ by invoiceId InvoiceId
83
+ filter status InvoiceStatus optional
84
+ example FirstIssued : InvoiceRow
85
+ invoiceId = "inv-1"
86
+ customerId = "customer-a"
87
+ status = issued
88
+ example SecondIssued : InvoiceRow
89
+ invoiceId = "inv-2"
90
+ customerId = "customer-b"
91
+ status = issued
92
+ example PaidInvoice : InvoiceRow
93
+ invoiceId = "inv-3"
94
+ customerId = "customer-a"
95
+ status = paid
96
+ specification OmittingAnOptionalFilter
97
+ given readmodel FirstIssued
98
+ given readmodel SecondIssued
99
+ given readmodel PaidInvoice
100
+ then query ListInvoices
101
+ arguments
102
+ status = issued
103
+ result
104
+ invoiceId = "inv-2"
105
+ result
106
+ invoiceId = "inv-1"
107
+ specification SupplyingBothFilters
108
+ given readmodel FirstIssued
109
+ given readmodel SecondIssued
110
+ then query ListInvoices
111
+ arguments
112
+ status = issued
113
+ customerId = "customer-a"
114
+ result
115
+ invoiceId = "inv-1"
116
+ specification CombiningByAndFilter
117
+ given readmodel FirstIssued
118
+ given readmodel SecondIssued
119
+ given readmodel PaidInvoice
120
+ when query InvoicesById
121
+ invoiceId = "inv-3"
122
+ status = issued
123
+ then no result
124
+ specification OmittingTheFilterBesideBy
125
+ given readmodel FirstIssued
126
+ given readmodel SecondIssued
127
+ given readmodel PaidInvoice
128
+ when query InvoicesById
129
+ invoiceId = "inv-3"
130
+ then result
131
+ invoiceId = "inv-3"
132
+ specification DistinguishingCallsByFilterValues
133
+ given readmodel FirstIssued
134
+ given readmodel PaidInvoice
135
+ then query ListInvoices
136
+ arguments
137
+ status = issued
138
+ result
139
+ invoiceId = "inv-1"
140
+ then query ListInvoices
141
+ arguments
142
+ status = paid
143
+ result
144
+ invoiceId = "inv-3"
145
+ ```
146
+
147
+ ## Argument diagnostics
148
+
149
+ This deliberately invalid fence pins PLAY0468 for missing, unknown and repeated
150
+ arguments in both specification forms. These are source errors, not passing
151
+ reference scenarios or empty-result assertions.
152
+
153
+ ```screenplay expect PLAY0468
154
+ concept ItemId : String
155
+ module FilteringExamples
156
+ feature Arguments
157
+ slice StateView Items
158
+ readmodel Item
159
+ itemId ItemId key
160
+ status String
161
+ query ListItems => Item[]
162
+ filter status String
163
+ specification MissingThenArgument
164
+ then query ListItems
165
+ specification MissingWhenArgument
166
+ when query ListItems
167
+ then no result
168
+ specification UnknownThenArgument
169
+ then query ListItems
170
+ arguments
171
+ status = "open"
172
+ search = "anything"
173
+ specification UnknownWhenArgument
174
+ when query ListItems
175
+ status = "open"
176
+ search = "anything"
177
+ then no result
178
+ specification RepeatedThenArgument
179
+ then query ListItems
180
+ arguments
181
+ status = "open"
182
+ status = "open"
183
+ specification RepeatedWhenArgument
184
+ when query ListItems
185
+ status = "open"
186
+ status = "open"
187
+ then no result
188
+ ```
189
+
190
+ ## Rendering is a different verdict
191
+
192
+ The latest Stage release checked here, **v4.80.0**, admits version pairs through
193
+ **ESM v13**, not v14, and its legacy query renderer also refuses authored filter
194
+ contracts. **CLI 3.53.1** bundles **Screenplay 4.137.0 / Stage 4.73.1**:
195
+ its compiler predates v14 and its renderer admits through v10. Standalone V3/V4
196
+ success therefore proves neither bundled binding nor rendering. A consumer must
197
+ explicitly admit v14 and preserve filter arguments and equality selection;
198
+ never erase filters to obtain a render.
199
+
200
+ ## Verified product sources
201
+
202
+ - Screenplay **v4.145.0**: accepted/implemented decision
203
+ `decisions/0069-admit-equality-query-filters-as-esm-v14.md`;
204
+ `Documentation/screenplay/queries.md` (Executable equality filters),
205
+ `specifications.md:145,307-319,409-428`; admission shipped in v4.143.0.
206
+ - Stage **v4.80.0**: `Source/Rendering.Cratis/Semantics/SemanticVersionFeatures.cs:95-100`
207
+ uses `EsmSchemaV13Support`; `Renderers/QueryAdmission.cs:13-22` refuses filters;
208
+ `Directory.Packages.props` pins Screenplay 4.141.0.
209
+ - Stage **v4.73.1**: `Source/Rendering.Cratis/Semantics/SemanticVersionFeatures.cs:89`
210
+ uses `EsmSchemaV10Support`. CLI **v3.53.1**:
211
+ `Directory.Packages.props:8,10` pins Screenplay 4.137.0 and Stage 4.73.1.
@@ -77,11 +77,8 @@ module Berths
77
77
  command BookBerth
78
78
  bookingId BookingId identifier
79
79
  berth BerthName
80
- produces BerthBooked
81
- for bookingId
82
- berth = berth
83
- event BerthBooked
84
- berth BerthName
80
+ produces event BerthBooked
81
+ berth BerthName = berth
85
82
  screen BookBerthScreen
86
83
  title "Book a berth"
87
84
  action BookBerth
@@ -7,6 +7,10 @@
7
7
  { "kind": "skill-contains", "value": "command reads only", "file": "references/named-keys.md" },
8
8
  { "kind": "skill-contains", "value": "remain authoring-only", "file": "references/named-keys.md" },
9
9
  { "kind": "play-compiles" },
10
+ { "kind": "skill-contains", "value": "Caller-supplied equality query filters (ESM v14)", "file": "references/equality-filters.md" },
11
+ { "kind": "skill-contains", "value": "Supplied `null` is refused", "file": "references/equality-filters.md" },
12
+ { "kind": "skill-contains", "value": "PLAY0468", "file": "references/equality-filters.md" },
13
+ { "kind": "skill-contains", "value": "without regard to row order", "file": "references/equality-filters.md" },
10
14
  { "kind": "skill-contains", "value": "main `fd18129`" },
11
15
  { "kind": "skill-contains", "value": "filter status InvoiceStatus optional" },
12
16
  { "kind": "skill-contains", "value": "query Q => observable?" },
@@ -30,7 +30,7 @@ ESM). Only the probe answers renderability. `PLAY0270` entries are information,
30
30
  | Class | Meaning | Typical examples (each verified in `admission.md`) | Action |
31
31
  | --- | --- | --- | --- |
32
32
  | **Model fix (in mode)** | the model is in, or the user chose, renderable mode and breaks its own subset in a way that has a renderable equivalent with the same meaning | a `produces` whose `for` is not the command identifier; one `StateChange` slice with two commands (`STAGE-ESM-004`); two projections for one read model (`-007`); a policy Stage cannot express portably (`-015`; role-only and opaque pure policies render at 4.73.1) | edit request to the modeler session with address, change and rationale |
33
- | **Capability gap** | the intended meaning needs a construct Stage does not render yet | a snapshot list query (`-010`; observable lists and keyed or unkeyed lists bind and observable lists render), and `filter`, `scoped to`, `performer` queries (`PLAY0268`); Automation and Translate slices (`-001`/`-024`, Stage#79); reactions, clocks, captures and triggers (`-024`); a reaction that both produces and invokes (`-037`: keep it whole and hand-write one reactor on Arc 22.72.0 or later (the Arc the scaffold pins needs a proven override), never split it; Stage#207); generated values and responses (`-028`/`-029`, Stage#175); `then no readmodel` (`-027`); evolved events (`STAGE-ESM-026`); conditional `produces when` (`-006`); `reads` (`PLAY0271`); code validation (`-005`); compliance attributes (`PLAY0268` "require portable data-subject semantics"); a command `handler` (`PLAY0268`) | keep the model; ledger entry; options a to c in `SKILL.md`; link the Stage issue |
33
+ | **Capability gap** | the intended meaning needs a construct Stage does not render yet | a snapshot list query (`-010`; observable lists and keyed or unkeyed lists bind and observable lists render), caller-supplied collection equality filters bind as v14 since 4.143.0 but exceed Stage 4.73.1's v10 ceiling and latest checked 4.80.0's v13 ceiling; context/single-result filters, opaque `scoped to`/`performer` and composite `by` remain PLAY0268; Automation and Translate slices (`-001`/`-024`, Stage#79); reactions, clocks, captures and triggers (`-024`); a reaction that both produces and invokes (`-037`: keep it whole and hand-write one reactor on Arc 22.72.0 or later (the Arc the scaffold pins needs a proven override), never split it; Stage#207); generated values and responses (`-028`/`-029`, Stage#175); `then no readmodel` (`-027`); evolved events (`STAGE-ESM-026`); conditional `produces when` (`-006`); `reads` (`PLAY0271`); code validation (`-005`); compliance attributes (`PLAY0268` "require portable data-subject semantics"); a command `handler` (`PLAY0268`) | keep the model; ledger entry; options a to c in `SKILL.md`; link the Stage issue |
34
34
  | **Tool skew** | the bundled compiler disagrees with documented semantics | the false `PLAY0285` on reaction cascades from a cratis before 3.28.2 bundle (cli#242); codes the standalone compiler does not raise | record in the session state and the ledger; never "fix" a correct model |
35
35
  | **Environment** | not the model | restore failures, missing SDK, a destination that is not writable | fix the environment or report blocked |
36
36
 
@@ -37,11 +37,8 @@ module Berths
37
37
  berthId BerthId identifier
38
38
  boatName BoatName
39
39
  authorize IsHarbourMaster
40
- produces BerthReserved
41
- for berthId
42
- boatName = boatName
43
- event BerthReserved
44
- boatName BoatName
40
+ produces event BerthReserved
41
+ boatName BoatName = boatName
45
42
  specification ReservingABerth
46
43
  given caller
47
44
  authenticated
@@ -141,7 +141,7 @@ target requirements.
141
141
  - Every slice has at least one specification at the application boundary.
142
142
  - One behaviour per spec; names read as sentences: `Reserving...`, `Rejecting...`, `Refusing...`
143
143
  (denial), `<View>LifecycleN<State>` for families.
144
- - `for` on every given, appended and expected event, and on the `when` command where the
144
+ - `for` on every given, appended and expected event (plain `produces <Name>` too; inline `produces event` omits it), and on the `when` command where the
145
145
  destination matters. A competing-claim given needs a *different* `for`.
146
146
  - Format rules belong on a `concept`: give each concept rule representative acceptance,
147
147
  rejection and boundary specs once (through one command that carries it), not on every use;
@@ -214,6 +214,9 @@ scope is reported as explicitly incomplete); V1 passes with the tool named in
214
214
  - V1 passes (standalone `screenplay` required when cascades are specified); report tool and
215
215
  version.
216
216
  - Run V4 through standalone `screenplay test` or MCP `run-specifications`;
217
+ on **Screenplay 4.145.0 and later**, propose → review → run with `proposalId` →
218
+ apply. PLAY0285 (impossible `then`) unbinds
219
+ the whole model; no spec runs.
217
220
  if not executed, report the actual host/input reason, not a blanket no-route
218
221
  claim. Stage's runner is target-engine evidence, never V4; V1/V3 do not prove pass. V5 is "not run" unless rendering was attempted; report admission, publication, build and
219
222
  tests separately.
@@ -41,17 +41,11 @@ module Berths
41
41
  boatLength > 0 message "Boat length must be positive"
42
42
  boatLength <= 24 message "Guest berths take boats up to 24 metres"
43
43
  night >= today message "The night must not be in the past"
44
- produces BerthReserved
45
- for reservationId
46
- berth = berth
47
- night = night
48
- boatName = boatName
49
- boatLength = boatLength
50
- event BerthReserved
51
- berth BerthCode
52
- night Date
53
- boatName String
54
- boatLength Decimal
44
+ produces event BerthReserved
45
+ berth BerthCode = berth
46
+ night Date = night
47
+ boatName String = boatName
48
+ boatLength Decimal = boatLength
55
49
  constraint OneBoatPerBerthNight
56
50
  unique berth, night on BerthReserved
57
51
  released by ReservationCancelled
@@ -226,11 +220,8 @@ module Berths
226
220
  reason not empty message "Say why the reservation is cancelled"
227
221
  require booking.status == "reserved"
228
222
  message "Only a live reservation can be cancelled"
229
- produces ReservationCancelled
230
- for reservationId
231
- reason = reason
232
- event ReservationCancelled
233
- reason String
223
+ produces event ReservationCancelled
224
+ reason String = reason
234
225
  constraint CancelOrDepart
235
226
  unique event ReservationCancelled
236
227
  unique event BoatDeparted
@@ -351,12 +342,10 @@ module Berths
351
342
  validate
352
343
  require booking.status == "reserved"
353
344
  message "Only a live reservation can record a departure"
354
- produces BoatDeparted
355
- for reservationId
345
+ produces event BoatDeparted
356
346
  produces when damageObserved == true
357
347
  BerthDamageReported
358
348
  for reservationId
359
- event BoatDeparted
360
349
  event BerthDamageReported
361
350
 
362
351
  specification RecordingACleanDeparture
@@ -46,13 +46,9 @@ module Invoicing
46
46
  invoiceId InvoiceId identifier
47
47
  member MemberId
48
48
  amount Decimal
49
- produces InvoiceIssued
50
- for invoiceId
51
- member = member
52
- amount = amount
53
- event InvoiceIssued
54
- member MemberId
55
- amount Decimal
49
+ produces event InvoiceIssued
50
+ member MemberId = member
51
+ amount Decimal = amount
56
52
  constraint IssueOnce
57
53
  unique event InvoiceIssued
58
54
 
@@ -115,8 +111,7 @@ module Invoicing
115
111
  validate
116
112
  require invoice.amount > 0
117
113
  message "Only an open invoice can be settled" // NOT enforced: the view lags (PLAY0268 at binding)
118
- produces InvoiceSettled
119
- for invoiceId
114
+ produces event InvoiceSettled
120
115
  command VoidInvoice
121
116
  invoiceId InvoiceId identifier
122
117
  reason String
@@ -124,12 +119,8 @@ module Invoicing
124
119
  validate
125
120
  require invoice.amount > 0
126
121
  message "Only an open, unpaid invoice can be voided" // NOT enforced: the view lags (PLAY0268 at binding)
127
- produces InvoiceVoided
128
- for invoiceId
129
- reason = reason
130
- event InvoiceSettled
131
- event InvoiceVoided
132
- reason String
122
+ produces event InvoiceVoided
123
+ reason String = reason
133
124
  constraint SettleOrVoid
134
125
  unique event InvoiceSettled
135
126
  unique event InvoiceVoided
@@ -179,11 +170,8 @@ module Invoicing
179
170
  validate
180
171
  require payment.invoiceId == invoiceId
181
172
  message "Only a received, not yet reversed payment can be reversed" // NOT enforced: the view lags (PLAY0268 at binding)
182
- produces PaymentReversed
183
- for invoiceId
184
- amount = amount
185
- event PaymentReversed
186
- amount Decimal
173
+ produces event PaymentReversed
174
+ amount Decimal = amount
187
175
 
188
176
  specification ReversingAReceivedPayment
189
177
  given caller