@cratis/pi 7.66.0 → 7.68.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 (51) hide show
  1. package/package/corpus/product-sync.json +3 -3
  2. package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/realization-and-gap-fill.md +1 -1
  3. package/package/corpus/skills/cratis-screenplay-captures-and-reactions/references/atomic-batches-and-source-mappings.md +1 -1
  4. package/package/corpus/skills/cratis-screenplay-command-surface/SKILL.md +4 -3
  5. package/package/corpus/skills/cratis-screenplay-command-surface/references/date-comparisons.md +13 -2
  6. package/package/corpus/skills/cratis-screenplay-model-authoring/references/language-reference.md +2 -2
  7. package/package/corpus/skills/cratis-screenplay-model-authoring/references/state-and-recovery.md +1 -0
  8. package/package/corpus/skills/cratis-screenplay-model-review/references/checklist.md +1 -1
  9. package/package/corpus/skills/cratis-screenplay-model-review/references/public-keys-and-routes.md +4 -2
  10. package/package/corpus/skills/cratis-screenplay-model-review/references/report-template.md +2 -2
  11. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/SKILL.md +1 -1
  12. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/phases.md +1 -1
  13. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/stop-or-assume.md +1 -1
  14. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/verdicts-and-modes.md +6 -1
  15. package/package/corpus/skills/cratis-screenplay-projections/SKILL.md +8 -1
  16. package/package/corpus/skills/cratis-screenplay-projections/references/pdl-grammar.md +42 -2
  17. package/package/corpus/skills/cratis-screenplay-projections/references/reducer-identity.md +88 -0
  18. package/package/corpus/skills/cratis-screenplay-projections/verification.json +2 -1
  19. package/package/corpus/skills/cratis-screenplay-read-surface/SKILL.md +14 -7
  20. package/package/corpus/skills/cratis-screenplay-read-surface/references/equality-filters.md +1 -1
  21. package/package/corpus/skills/cratis-screenplay-read-surface/references/named-keys.md +168 -58
  22. package/package/corpus/skills/cratis-screenplay-read-surface/verification.json +2 -2
  23. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/gap-fill-handoff.md +1 -1
  24. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/renderability-gate.md +1 -1
  25. package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/scenario-examples.md +2 -2
  26. package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/spec-forms-by-mode.md +3 -3
  27. package/package/corpus/skills/cratis-screenplay-slice-design/SKILL.md +5 -7
  28. package/package/corpus/skills/cratis-screenplay-slice-design/references/public-boundaries-and-keys.md +5 -3
  29. package/package/corpus/skills/cratis-screenplay-slice-design/references/read-model-design.md +7 -3
  30. package/package/corpus/skills/cratis-screenplay-specifications/SKILL.md +3 -2
  31. package/package/corpus/skills/cratis-screenplay-specifications/references/query-rejections.md +4 -0
  32. package/package/corpus/skills/cratis-screenplay-specifications/references/vocabulary.md +17 -0
  33. package/package/corpus/skills/cratis-screenplay-streams-and-consistency/SKILL.md +3 -1
  34. package/package/corpus/skills/cratis-screenplay-streams-and-consistency/references/read-model-keys.md +46 -0
  35. package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +11 -5
  36. package/package/corpus/skills/cratis-screenplay-toolchain/references/cheat-sheet.md +17 -2
  37. package/package/corpus/skills/cratis-screenplay-toolchain/references/diagnostics.md +22 -8
  38. package/package/corpus/skills/cratis-screenplay-toolchain/references/executable-subset.md +31 -9
  39. package/package/corpus/skills/cratis-screenplay-toolchain/references/language-admission.md +15 -5
  40. package/package/corpus/skills/cratis-screenplay-toolchain/references/production-routes-and-filters.md +1 -1
  41. package/package/corpus/skills/cratis-screenplay-toolchain/references/renderable-subset.md +3 -3
  42. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +1722 -76
  43. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
  44. package/package/corpus/skills/cratis-screenplay-toolchain/references/traps.md +3 -2
  45. package/package/corpus/skills/cratis-screenplay-toolchain/references/verdicts.md +64 -3
  46. package/package/corpus/skills/cratis-screenplay-toolchain/verification.json +8 -2
  47. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +5 -4
  48. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +12 -7
  49. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/sandbox-and-specrunner.md +17 -0
  50. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/verification.json +1 -1
  51. package/package.json +1 -1
@@ -171,9 +171,9 @@
171
171
  }
172
172
  ],
173
173
  "synced": {
174
- "version": "4.201.0",
174
+ "version": "4.204.0",
175
175
  "date": "2026-10-16",
176
- "by": "Cratis/AI#733"
176
+ "by": "Cratis/AI#743"
177
177
  },
178
178
  "covers": {
179
179
  "skills": [
@@ -201,7 +201,7 @@
201
201
  ],
202
202
  "rules": []
203
203
  },
204
- "notes": "Standalone 4.201.0 and byte-identical compiler contract pinned. Cratis/AI#733: the ui profile root template, shell options, appearance and new screens default (PLAY0788-PLAY0799, quick fix for the legacy layout line, ui-profiles MCP tool, 44 tools). Cratis/AI#735: chronological Date/DateTime ordering in command conditions and command/property literal validation (ESM v26), UTC tick precision and pre-rule offset validation; then matching remains ordinal. Source-checked Screenplay v4.199.0 commands, diagnostics, interoperability and specifications documentation plus decision 0087. Stage v4.127.1 still refuses v26 with STAGE-ESM-016 (stage-rendering.md and SemanticVersionFeatures.Supports); no render/runtime probe. Cratis/AI#576: prior mechanical compiler catch-up. Language batch #717/#722/#723/#724/#720/#728/#734 teaches atomic reaction batches (v22), described payload handlers (v23), portable linear matching, ordinal text rules (v24), protected command reads (v25), diagnostic arguments and event-source invokes mappings. Facts read at Screenplay v4.198.0 and introducing tags; Stage v4.127.0 admits exact v1-v21 and v23-v25 pairs but refuses v22/v26 and mixed pairs (STAGE-ESM-016), protected reads (044), atomic mode (043) and described handlers (STAGE-IMPL-002), even Stub mode. Stage text rules execute/render; no new renderer/runtime probe or Stage pin uplift. Cratis/AI#706: application navigation regions (module inherit, extend or override), toolbar slice buttons, blueprint template references and sample data packs in cratis-screenplay-ui-composition (references/application-shell.md), with the canonical shell vector and the rejection codes PLAY0710-PLAY0722 and PLAY0740-PLAY0765. Examples moved to the executable-state rules since 4.164: translator reactions in an Automation slice beside the Translate slice (PLAY0694) and the first record of a capture key appended with `when added`. Earlier: Standalone 4.164.0 and byte-identical compiler contract pinned. Cratis/AI#663: one origin per capture (PLAY0687). #668: executable no-event/redelivery v17. #684: concept query-argument validation in every version and query-error assertions v18. #681: executable refusal branches and refusal values v19. #666: proposal summary/severity counts and exact structural source proposals. Generated-concept rules select v20 with PLAY0688/0689 proofs and derived-matches repair; generated secrets and preview strategies remain refused. Release-baseline from-esm admission reads storedName. Standalone binding is not renderer admission: Stage 4.103.0 admits v1-v17 and v19; CLI 3.62.0 bundles Screenplay 4.159.0 / Stage 4.99.0 and renders through v16. Prior evidence remains unless superseded; no new renderer, Docker or browser probe."
204
+ "notes": "Standalone 4.204.0 and byte-identical compiler contract pinned. Cratis/AI#702 and #691: PLAY0690 reducer identity (Screenplay 4.170.0, native compiler only) and the screenplay.json validation gate (Screenplay 4.165.0; cratis screenplay validate reads it from CLI 3.65.0) are taught and probed against the 4.204.0 compiler. Cratis/AI#743: flat composite read-model keys (ESM v27): untyped projection key blocks on root from and remove with, equality-filter lists, single-instance by blocks and per-part fixtures, with PLAY0661/PLAY0662/PLAY0268 refusals checked against the 4.204.0 compiler; Stage origin/main still refuses v27 with STAGE-ESM-016 (stage-rendering.md); Stage v4.135.0 v26 admission (Host and executor run date comparisons; renderer STAGE-ESM-046) was source-checked for this sync; no render or runtime probe. Prior: Standalone 4.201.0 and byte-identical compiler contract pinned. Cratis/AI#733: the ui profile root template, shell options, appearance and new screens default (PLAY0788-PLAY0799, quick fix for the legacy layout line, ui-profiles MCP tool, 44 tools). Cratis/AI#735: chronological Date/DateTime ordering in command conditions and command/property literal validation (ESM v26), UTC tick precision and pre-rule offset validation; then matching remains ordinal. Source-checked Screenplay v4.199.0 commands, diagnostics, interoperability and specifications documentation plus decision 0087. Stage v4.127.1 still refuses v26 with STAGE-ESM-016 (stage-rendering.md and SemanticVersionFeatures.Supports); no render/runtime probe. Cratis/AI#576: prior mechanical compiler catch-up. Language batch #717/#722/#723/#724/#720/#728/#734 teaches atomic reaction batches (v22), described payload handlers (v23), portable linear matching, ordinal text rules (v24), protected command reads (v25), diagnostic arguments and event-source invokes mappings. Facts read at Screenplay v4.198.0 and introducing tags; Stage v4.127.0 admits exact v1-v21 and v23-v25 pairs but refuses v22/v26 and mixed pairs (STAGE-ESM-016), protected reads (044), atomic mode (043) and described handlers (STAGE-IMPL-002), even Stub mode. Stage text rules execute/render; no new renderer/runtime probe or Stage pin uplift. Cratis/AI#706: application navigation regions (module inherit, extend or override), toolbar slice buttons, blueprint template references and sample data packs in cratis-screenplay-ui-composition (references/application-shell.md), with the canonical shell vector and the rejection codes PLAY0710-PLAY0722 and PLAY0740-PLAY0765. Examples moved to the executable-state rules since 4.164: translator reactions in an Automation slice beside the Translate slice (PLAY0694) and the first record of a capture key appended with `when added`. Earlier: Standalone 4.164.0 and byte-identical compiler contract pinned. Cratis/AI#663: one origin per capture (PLAY0687). #668: executable no-event/redelivery v17. #684: concept query-argument validation in every version and query-error assertions v18. #681: executable refusal branches and refusal values v19. #666: proposal summary/severity counts and exact structural source proposals. Generated-concept rules select v20 with PLAY0688/0689 proofs and derived-matches repair; generated secrets and preview strategies remain refused. Release-baseline from-esm admission reads storedName. Standalone binding is not renderer admission: Stage 4.103.0 admits v1-v17 and v19; CLI 3.62.0 bundles Screenplay 4.159.0 / Stage 4.99.0 and renders through v16. Prior evidence remains unless superseded; no new renderer, Docker or browser probe."
205
205
  },
206
206
  {
207
207
  "id": "scene",
@@ -23,7 +23,7 @@ rejected model renderable, and never claim a whole-application V5 from a subset.
23
23
  with `cratis-screenplay-render-and-gap-fill`.
24
24
 
25
25
  Application-root `reaction batches atomic` also blocks Stage delivery: v22
26
- refuses STAGE-ESM-016; mode in admitted v23–v25 refuses STAGE-ESM-043. Preserve the
26
+ refuses STAGE-ESM-016; mode in admitted v23–v26 refuses STAGE-ESM-043. Preserve the
27
27
  per-occurrence/selected-branch batch boundary and independent invoked commands.
28
28
  Atomic appends do not prove redelivery deduplication or external exactly-once effects.
29
29
 
@@ -36,7 +36,7 @@ batch and unchanged read models with evaluator world/read-model assertions; earl
36
36
  accepted work can remain. `then no events` means accepted zero outcomes and cannot
37
37
  accompany `then error`. Do not invent a mixed success/error specification form.
38
38
 
39
- Stage refuses ESM v22 with STAGE-ESM-016. Even an admitted v23–v25 model carrying
39
+ Stage refuses ESM v22 with STAGE-ESM-016. Even an admitted v23–v26 model carrying
40
40
  atomic mode is refused with STAGE-ESM-043 by renderer, Host and specification executor.
41
41
  Keep the declaration and report authorized gap-fill; do not promise a render date or
42
42
  strip atomicity to lower the version.
@@ -155,8 +155,9 @@ reads/concurrency cannot combine (PLAY0783). The Cratis C# renderer refuses prot
155
155
  semantic Host/executor support is separate.
156
156
  Uniqueness still uses a constraint; reference success is not target realization.
157
157
 
158
- Do not use `reads` to fetch data the caller could supply. A composite-key view's `by` block is
159
- `PLAY0268` (read-surface `references/named-keys.md`).
158
+ Do not use `reads` to fetch data the caller could supply. A composite-key view's `by` block in a
159
+ command read is `PLAY0271`, or `PLAY0781` ByParts under `command reads protected`
160
+ (read-surface `references/named-keys.md`).
160
161
 
161
162
  ## `validate`
162
163
 
@@ -182,7 +183,7 @@ Requires Screenplay 4.199.0 (ESM v26; `screenplay --version`) for Date/DateTime
182
183
  comparisons; older binders report PLAY0268. Read [date comparisons](references/date-comparisons.md)
183
184
  when ordering a retention window with `require to >= from`, comparing instants
184
185
  across offsets, or rejecting offset-less inputs before rules. It covers admitted
185
- positions, PLAY0268/0273, ordinal `then` matching and Stage's v26 refusal.
186
+ positions, PLAY0268/0273, ordinal `then` matching and Stage's STAGE-ESM-046 renderer refusal.
186
187
 
187
188
  **Rules whose logic is code.** A bare `rule <Name>` has no portable meaning (`PLAY0268`) and a bodied rule binds only as
188
189
  opaque code. Read [references/validate-rules.md](references/validate-rules.md) when a rule's logic must live in a `file`
@@ -3,6 +3,11 @@
3
3
 
4
4
  # Chronological command date comparisons
5
5
 
6
+ ## Contents
7
+
8
+ - Retention window and rejection specifications
9
+ - Missing-offset input rejection
10
+
6
11
  Requires Screenplay 4.199.0 (ESM v26; `screenplay --version`); older binders refuse date comparisons with PLAY0268. Upgrade the compiler without weakening the rule.
7
12
 
8
13
  Use `==`, `!=`, `>`, `>=`, `<` or `<=` with matching primitives: Date/Date or
@@ -27,13 +32,19 @@ Targets must enforce this before deserialization attaches a local offset, and
27
32
  preserve tick precision or refuse the comparison; JavaScript `Date` alone truncates
28
33
  precision. Client validators omit date ordering rules. Optional absent values still
29
34
  pass validation rules other than `not empty`; optional condition operands refuse.
35
+ A .NET host that executes commands itself can reuse the reference evaluator's date semantics (Requires Screenplay 4.203.0): see [date comparison checks](https://github.com/Cratis/Screenplay/blob/main/Documentation/screenplay/date-comparison-checks.md) for `SemanticDateComparisons` and `SemanticConditionEvaluator`.
30
36
 
31
37
  PLAY0268 still refuses mixed Date/DateTime, `$context` operands (including
32
38
  `$context.occurred`), `today`, protected read paths, date rules on concepts and
33
39
  reaction `where`. A refused whole comparison reports only PLAY0268, without
34
40
  validating its literals. A `given clock` does not admit `today` or now.
35
- Stage refuses v26 with STAGE-ESM-016; standalone binding/reference execution is
36
- not rendering. Preserve the rule and route delivery to authorized gap-fill.
41
+ Requires Stage 4.135.0 for v26 admission; older Stage refuses v26 with STAGE-ESM-016.
42
+ The semantic Host and specification executor run the comparisons, including through
43
+ concepts, and reject an offset-less DateTime input after authorization and before any
44
+ rule. The renderer, legacy engines and default sandbox engine refuse them with
45
+ STAGE-ESM-046, naming the property, and emit no artifacts or date validators. Standalone
46
+ binding/reference execution is not rendering: preserve the rule and route delivery to
47
+ authorized gap-fill.
37
48
 
38
49
  ## Retention window and rejection specifications
39
50
 
@@ -272,9 +272,9 @@ See read-surface `references/named-keys.md` for the collection-key exclusion alg
272
272
  | Described handlers (ESM v23) | Pending implementation beside unconditional produces; payload-only contract, reached body Unsupported ImplementationBody; Stage STAGE-IMPL-002 even Stub mode |
273
273
  | Ordinal text rules (ESM v24) | contains/not contains/starts with/ends with on scalar non-enum text properties; quoted literals, not conditions; Stage semantic engines execute/render |
274
274
  | Protected command reads (ESM v25) | Root `command reads protected`; eligible read paths in requirements and production mappings, absence Validation, concurrent matching facts Conflict; Stage admits v25; Cratis C# rendering refuses STAGE-ESM-044, semantic Host/executor execute admitted reads with non-Error protected requirements refused STAGE-ESM-045 |
275
- | Chronological command dates (ESM v26) | Same-primitive Date/DateTime `==`, `!=`, `>`, `>=`, `<`, `<=` in require/produces-when (property or literal RHS) and command/property rules (literal RHS only). DateTime uses UTC ticks, participating offset-less inputs reject Validation before rules; then matching stays ordinal. Requires Screenplay 4.199.0 (`screenplay --version`); older binders refuse PLAY0268. Stage refuses v26 STAGE-ESM-016. Read command-surface `references/date-comparisons.md`. |
275
+ | Chronological command dates (ESM v26) | Same-primitive Date/DateTime `==`, `!=`, `>`, `>=`, `<`, `<=` in require/produces-when (property or literal RHS) and command/property rules (literal RHS only). DateTime uses UTC ticks, participating offset-less inputs reject Validation before rules; then matching stays ordinal. Requires Screenplay 4.199.0 (`screenplay --version`); older binders refuse PLAY0268. The Stage Host and executor run v26 (older Stage refuses STAGE-ESM-016); the renderer and legacy engines refuse STAGE-ESM-046 (toolchain `language-admission.md`). Read command-surface `references/date-comparisons.md`. |
276
276
  | Current routing and actor admission | Sources/streams and command/specification routes bind and reference-execute as v8; reaction `runs as system role` supplies the invocation actor as v10. Whole-workspace binding determines executable availability; CLI/Stage realization remains separate |
277
- | Blocks binding (`PLAY0268`) | enum/identity compliance and events combining personal or explicitly subject-scoped secret values with explicit subject marks; a compiler that stops before v13 refuses all `pii`/`secret` concepts ("compliance attributes require portable data-subject semantics"), while v13 binds them and Stage renders protection; attached command `handler` (PLAY0035 beside productions; pending described intent instead binds v23), bare `rule <Name>`, read paths without protected opt-in or in unsupported positions such as `produces when`, date comparisons with mixed primitives, optional condition operands, `$context`, protected read paths or `today`, date rules on concepts or reaction `where`, `$context.tenant`/claims/roles/causation in `produces`, `$env` conditions, `file` constraints, unquoted `import` of an external contract, a read model with neither explicit `key` nor unambiguous query-based identity as described above (composite keys remain unadmitted), queries with context `from` sources (typed query context is unadmitted), single-result filters (collections only), `scoped to` (opaque scope), `performer` (opaque implementation) or composite `by` blocks; optional keyed and collection queries, including caller-supplied equality filters and observable delivery, bind, `$causedBy` and templates in projections |
277
+ | Blocks binding (`PLAY0268`) | enum/identity compliance and events combining personal or explicitly subject-scoped secret values with explicit subject marks; a compiler that stops before v13 refuses all `pii`/`secret` concepts ("compliance attributes require portable data-subject semantics"), while v13 binds them and Stage renders protection; attached command `handler` (PLAY0035 beside productions; pending described intent instead binds v23), bare `rule <Name>`, read paths without protected opt-in or in unsupported positions such as `produces when`, date comparisons with mixed primitives, optional condition operands, `$context`, protected read paths or `today`, date rules on concepts or reaction `where`, `$context.tenant`/claims/roles/causation in `produces`, `$env` conditions, `file` constraints, unquoted `import` of an external contract, a read model with neither explicit `key` nor unambiguous query-based identity as described above (several key marks bind as v27; older binders refuse PLAY0268; toolchain `language-admission.md`), queries with context `from` sources (typed query context is unadmitted), single-result filters (collections only), `scoped to` (opaque scope), `performer` (opaque implementation) or a `by` block on a list query or non-optional single result; optional keyed and collection queries, including caller-supplied equality filters and observable delivery, bind, `$causedBy` and templates in projections |
278
278
  | Blocks binding (`PLAY0271`, legacy meaning) | Command reads without protected opt-in and `concurrency`; combined protected reads/concurrency is PLAY0783 |
279
279
  | Deferred (`PLAY0269`, information) | Screens, layouts, templates, forms, contributions, UI profiles, themes, behaviors and `on`/`uses` |
280
280
  | Metadata only (`PLAY0270`, information) | `domain`, `seed`, `persona`, trigger and other descriptions, and `file` provenance on declarations |
@@ -4,6 +4,7 @@ This reference holds identity-state, journal and recovery rules and the readines
4
4
 
5
5
  ## Preserve state and recover explicitly
6
6
 
7
+ `.screenplay/` is tool-owned; the only authored file beside it is `screenplay.json`, the validation gate, which no MCP operation writes.
7
8
  Commit `.screenplay/identities.json` with the model. Only `apply` writes it;
8
9
  `open-workspace` never creates it; text-only models have none. Never delete or
9
10
  overwrite conflicting identity state, silently mint IDs on restart, or text-rename
@@ -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 a collection (`=> RM[]`, observable too) with caller-supplied `by` and/or equality filters. `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: supported v14 equality filters render within the CLI bundle's ESM ceiling. Renderable scope only: Stage-specific identity/filter limits; keyed optional snapshot lookups and snapshot/observable collections render (Requires Stage 4.90.0 for snapshot lists; older renderers report `STAGE-ESM-010`). Check query concept-validation parity separately; primitive-argument targets answer absence instead of Validation | 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. `from`, single-result filters, opaque `scoped to`/`performer` and a list-query `by` block remain PLAY0268. A read model with several key parts binds as v27 (Requires Screenplay 4.204.0). The projection routes by the same identity (a `from` key or an event property, or `$eventSourceId`; `$eventSourceId` is not mandated). Filtered models: supported v14 equality filters render within the CLI bundle's ESM ceiling. Renderable scope only: Stage-specific identity/filter limits; keyed optional snapshot lookups and snapshot/observable collections render (Requires Stage 4.90.0 for snapshot lists; older renderers report `STAGE-ESM-010`). Check query concept-validation parity separately; primitive-argument targets answer absence instead of Validation | 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
 
@@ -16,8 +16,10 @@ Add these to the element sweep, with evidence pinned to the declaration:
16
16
  - Is the view identity explicitly marked `key` or unambiguously inferred? Does each
17
17
  single-instance query/read supply every part exactly once, with compatible types,
18
18
  and do state fixtures contain all parts? Do PDL keys align with the view's key?
19
- Composite view keys still refuse binding (PLAY0268); compilation
20
- is not executable evidence. Read read-surface `references/named-keys.md`.
19
+ Flat composite view keys bind as ESM v27 (older binders refuse with PLAY0268;
20
+ compiler requirement in read-surface `references/named-keys.md`); compilation alone is not executable evidence. Does
21
+ each projection `from` and `remove with` name every part in an untyped `key` block?
22
+ Read read-surface `references/named-keys.md`.
21
23
  - Does a production override replace the whole source/stream/id route, rather than
22
24
  assume partial inheritance? Is its `for` destination still explicit and compatible?
23
25
  Are skipped production routes valid, so eager resolution cannot fail unexpectedly?
@@ -167,8 +167,8 @@ it. Verify the answer became a specification, a description or a project-owned
167
167
  decision record; a resolved note is not behavioral proof. Ask the owner to reanchor
168
168
  or resolve affected notes before source apply; resolution alone leaves an orphan.
169
169
 
170
- The optional `--open-questions fail` gate is a standalone `screenplay` option; the
171
- CLI-bundled compiler can lack it. Report it separately from V1–V5. Evidence: Screenplay
170
+ The optional `--open-questions fail` gate exists on the standalone `screenplay` tool and on
171
+ `cratis screenplay validate` (CLI 3.60.0 or later). Report it separately from V1–V5. Evidence: Screenplay
172
172
  `Documentation/screenplay/model-notes.md`,
173
173
  `Documentation/screenplay/mcp/reference.md`; source lines in the linked guide.
174
174
 
@@ -75,7 +75,7 @@ or **critic** (P4-P5: strict, report first, fix in a separate batch).
75
75
  ## Verdicts (independent evidence, not a ladder)
76
76
  | Verdict | Meaning | Does not prove |
77
77
  |---|---|---|
78
- | V1 authorable | compiles, warnings as errors | meaning, binding |
78
+ | V1 authorable | compiles, warnings as errors (declared gate) | meaning, binding |
79
79
  | V2 executable diagnostics | the executable model produces none | that specs run |
80
80
  | V3 binding-ready | the model binds | that any spec passes |
81
81
  | V4 reference specs run | engine, expected vs discovered | rendering or build health |
@@ -109,7 +109,7 @@ phase, a decision important enough that a later reader could misread the model (
109
109
  ## P6 Accept (the user)
110
110
  - **Input**: the reviewed source identity.
111
111
  - **Carry-forward**: the accepted **source identity** (`verdicts-and-modes.md`).
112
- - **Commit at P6**: the accepted `.play` files and `.screenplay/identities.json` when present.
112
+ - **Commit at P6**: the accepted `.play` files, `screenplay.json` (the authored validation gate) when the repository has one, and `.screenplay/identities.json` when present.
113
113
  The user commits, or explicitly authorizes the owning session to commit; the modeler never
114
114
  commits on its own. Run `cratis-screenplay-modeling-lifecycle`'s `scripts/model-first-status.mjs` to check acceptance, drafts and model changes in progress.
115
115
  - **Gate**: explicit acceptance and the model commit before P7-P9. After committing, recompute
@@ -64,7 +64,7 @@ consequential policy. Resolving is not implementation; a known refusal still
64
64
  needs `then error` or `then denied`. Reopen when the evidence ceases to answer it.
65
65
 
66
66
  Question gating is separate from V1: standalone Screenplay 4.144.0 has
67
- `--open-questions fail`; Cratis CLI 3.53.1 does not. Read toolchain
67
+ `--open-questions fail`, and `cratis screenplay validate` has it from CLI 3.60.0. Read toolchain
68
68
  `references/verdicts.md` "Model notes gate" when a phase requires no open questions.
69
69
  Product evidence: Screenplay `Documentation/screenplay/model-notes.md`,
70
70
  `decisions/0045-model-notes-sidecar.md` and the model-authoring source table.
@@ -48,6 +48,11 @@ V5 rules:
48
48
  ## Commands (enough to check without loading the toolchain skill)
49
49
  - V1, preferred: `screenplay <model-folder> --warnaserror --no-color` (check "N file(s) compiled").
50
50
  - V1, fallback: `cratis screenplay validate <model-folder> --warnings-as-errors`.
51
+ - The repository's gate file: V1 runs through a `screenplay.json` at the model root when one exists
52
+ (checks, warnings as errors, scope, open questions), so it is part of the verification contract.
53
+ Read the effective gate with `screenplay <model-folder> --print-gate`, name it on the V1 line, and
54
+ never bypass it with `--no-gate-file` without saying so. A refused file is `V1 not run: gate file
55
+ refused`, not a pass. Commands and precedence: toolchain `references/verdicts.md`.
51
56
  - V2, V3: the MCP workspace readiness and executable diagnostics.
52
57
  - Name tool and version on each line. A tool older than the model's constructs reports false
53
58
  failures; see the toolchain skill.
@@ -56,7 +61,7 @@ V5 rules:
56
61
 
57
62
  ```text
58
63
  Mode: executable Source: <commit+digest from the source-identity helper> / ws <revision> cat <catalogRevision>
59
- V1 authorable: pass (N files) | fail: PLAYnnnn at file:line - meaning
64
+ V1 authorable: pass (N files, gate: <screenplay.json or flags>) | fail: PLAYnnnn at file:line - meaning | not run: gate file refused
60
65
  V2 diagnostics: none (executable model) | n diagnostics | not run: <reason>
61
66
  V3 binding: ready | blocked: PLAYnnnn ... | not run: design mode
62
67
  V4 specs: 12 expected / 12 discovered, 11 passed, 1 unsupported (route) | not run: no route
@@ -100,6 +100,7 @@ the executable model routes any event by it, and it is not a default for the
100
100
  | From-level block | indented `key invoiceId` inside the `from` |
101
101
  | Per event, several events | `from A key idA, B key idB, C` |
102
102
  | Composite | `key OrderKey` with indented `<property> = <expr>` parts |
103
+ | Flat composite parts | untyped `key` block naming every key part (Requires 4.204.0; ESM v27) |
103
104
  | Literal (constant) | `key literal "site-stats"` — every event updates the **same** instance |
104
105
  | Children identity | `children lines identified by lineNumber` |
105
106
  | **Default** | **the event source id** — `from X` ≡ `from X key $eventSourceId` |
@@ -120,6 +121,10 @@ subscribe to it exactly like a standalone event. Verified extraction preserves
120
121
  canonical ESM and identities, so it changes no projection contract. Removing a
121
122
  payload property is different and requires consumer/persistence review.
122
123
 
124
+ A read model with several flat `key` parts needs that block in every root `from` and
125
+ `remove with` (`PLAY0661`/`PLAY0662`); other blocks and reducers there are `PLAY0268`
126
+ (`references/pdl-grammar.md`).
127
+
123
128
  A second key on one `from` is error `PLAY0060`. A template expression in a
124
129
  composite key is error `PLAY0073`; an empty composite key is `PLAY0074`.
125
130
 
@@ -256,7 +261,9 @@ What the executable model does with it (ESM v3):
256
261
  rules is `PLAY0398`; two rules for one event is `PLAY0399`; a reducer with no
257
262
  bodies is rejected with a hint to write a projection.
258
263
  - The key is **always the event source id** — a reducer cannot route by an event
259
- property. State starts at null; returning null deletes the instance.
264
+ property. State starts at null; returning null deletes the instance. A query
265
+ `by` another identity type warns `PLAY0690`; use a projection keyed on each `from`
266
+ or a join ([reducer identity](references/reducer-identity.md)).
260
267
  - The body is an opaque attachment identified by a content hash, never compiled
261
268
  or run by Screenplay. The reference runner cannot compute reducer state, so a
262
269
  specification that reads a reducer-built read model reports unsupported and
@@ -6,6 +6,7 @@
6
6
  - Parser regexes
7
7
  - Diagnostics
8
8
  - Syntax that parses but means something else
9
+ - Flat composite read-model keys
9
10
  - Worked examples
10
11
 
11
12
  Checked against `Documentation/screenplay/projections/{grammar,semantic-model,keys,variants}.md`
@@ -69,11 +70,11 @@ NestedBlock = "nested", Ident, NL,
69
70
  ClearWithBlock = "clear", "with", TypeRef, NL ;
70
71
 
71
72
  RemoveWithBlock = "remove", "with", TypeRef, [ "key", Expr ], NL,
72
- [ INDENT, [ ParentDecl ], DEDENT ] ;
73
+ [ INDENT, [ CompositeKeyDecl ], [ ParentDecl ], DEDENT ] ;
73
74
  RemoveWithJoinBlock = "remove", "via", "join", "on", TypeRef, [ "key", Expr ], NL ;
74
75
 
75
76
  KeyDecl = "key", Expr, NL ;
76
- CompositeKeyDecl= "key", TypeRef, [ "{" ], NL,
77
+ CompositeKeyDecl= "key", [ TypeRef ], [ "{" ], NL,
77
78
  INDENT, KeyPart, { NL, KeyPart }, DEDENT, [ "}", NL ] ;
78
79
  KeyPart = Ident, "=", Expr ;
79
80
 
@@ -172,6 +173,45 @@ LiteralKeyword = "literal", " ", Literal ; (* parsed at the Expr level *)
172
173
  | `all` beside removals, `children` or `nested` | Binds, but the reference execution plan refuses it |
173
174
  | `join`, `children` or `remove via join` inside `nested` | Not wired by Chronicle's engine; binds, but the reference execution plan refuses it |
174
175
 
176
+ ## Flat composite read-model keys
177
+
178
+ Requires Screenplay 4.204.0 (ESM v27; `screenplay --version`); older binders refuse a
179
+ read model with two or more `key` marks with PLAY0268.
180
+
181
+ A read model with several flat `key` properties takes an **untyped** `key` block (no
182
+ type name) in each root `from` and `remove with`, naming every key part once as
183
+ `<property> = <expr>`. It differs from the typed composite key above, which fills one
184
+ single property of composite type.
185
+
186
+ ```screenplay excerpt
187
+ projection PaymentTotals => PaymentTotals
188
+ no automap
189
+ from PaymentReceived
190
+ key
191
+ invoiceId = invoice
192
+ method = method
193
+ total = amount
194
+ remove with PaymentRemoved
195
+ key
196
+ invoiceId = invoice
197
+ method = method
198
+ ```
199
+
200
+ The block applies to every event the `from` names. Part order does not matter; creating
201
+ an instance writes each part to its property even with `no automap`, and a mapping that
202
+ contradicts a part fails the projection. `remove with` deletes only the instance whose
203
+ parts all match, and a later `from` recreates it.
204
+
205
+ | Written | Code |
206
+ | --- | --- |
207
+ | `from` or `remove with` with no key, `key <expr>`, an inline `from A key x`, or `parent` | PLAY0661 |
208
+ | A part missing from the block | PLAY0661 |
209
+ | An unknown, repeated or ill-typed part, a non-simple expression, or a typed key block on a composite-keyed read model | PLAY0662 |
210
+ | `join`, `children`, `remove via join`, `every`, `all`, `nested`, `clear with`, `variant`, or a reducer on the read model | PLAY0268 |
211
+
212
+ The complete executable example, with queries and specifications, is in read-surface
213
+ `references/named-keys.md`.
214
+
175
215
  ## Worked examples
176
216
 
177
217
  Each example is an excerpt: the events and read models it names are declared
@@ -0,0 +1,88 @@
1
+ # Reducer instance identity (PLAY0690)
2
+
3
+ A reducer instance is keyed by the **event-source id** of the event it reduces. A reducer cannot be re-keyed by a payload value, a read-model `key` or a stream partition. A view that must be looked up by anything other than that event-source identity needs a projection.
4
+
5
+ Requires Screenplay 4.170.0 or later (`screenplay --version`) for PLAY0690; an older compiler does not report it, so a clean run there does not clear the mismatch.
6
+
7
+ ## What PLAY0690 reports
8
+
9
+ A warning (it fails `--warnaserror`) in two cases:
10
+
11
+ - A reducer-backed query returns one instance `by` a scalar whose nominal type differs from the identity type of an observed event whose producers are all known, for example `by month Month` while the events live on `ResourceId` streams.
12
+ - The producers of one reducer's events use several different nominal identity types, so no single identity can key the instances. One reducer warning replaces the repeated query warnings.
13
+
14
+ This example reports PLAY0690 on `ByMonth`: the instances follow `ResourceId`, not `Month`.
15
+
16
+ ```screenplay expect PLAY0690
17
+ concept ResourceId : String
18
+ concept Month : String
19
+ module Work
20
+ feature Reporting
21
+ slice StateView Totals
22
+ event Recorded
23
+ month Month
24
+ hours Decimal
25
+ readmodel Totals
26
+ month Month key
27
+ hours Decimal
28
+ reducer Fold => Totals
29
+ on Recorded
30
+ ```csharp
31
+ return context.State is null
32
+ ? new(context.Event.month, context.Event.hours)
33
+ : context.State with { hours = context.State.hours + context.Event.hours };
34
+ ```
35
+ query ByMonth => Totals optional
36
+ by month Month
37
+ slice StateChange Record
38
+ command Record
39
+ resource ResourceId identifier
40
+ month Month
41
+ hours Decimal
42
+ produces Recorded
43
+ for resource
44
+ month = month
45
+ hours = hours
46
+ ```
47
+
48
+ ## Fix
49
+
50
+ To aggregate by a payload value, replace the reducer with a projection that declares a property key on each `from`. The read-model `key` and the `from` key then agree, and the warning is gone:
51
+
52
+ ```screenplay
53
+ concept ResourceId : String
54
+ concept Month : String
55
+ module Work
56
+ feature Reporting
57
+ slice StateView Totals
58
+ event Recorded
59
+ month Month
60
+ hours Decimal
61
+ readmodel Totals
62
+ month Month key
63
+ hours Decimal
64
+ projection Fold => Totals
65
+ from Recorded key month
66
+ add hours by hours
67
+ query ByMonth => Totals optional
68
+ by month Month
69
+ slice StateChange Record
70
+ command Record
71
+ resource ResourceId identifier
72
+ month Month
73
+ hours Decimal
74
+ produces Recorded
75
+ for resource
76
+ month = month
77
+ hours = hours
78
+ ```
79
+
80
+ When instances already exist under their own identity and another event only enriches them, use a [join](joins.md) instead: it updates the existing instances and never creates one. There is no automatic repair from a reducer to a projection.
81
+
82
+ ## What the check compares
83
+
84
+ - It compares declared **nominal** identity types (`Month` and `ResourceId` differ even though both wrap text), never property names, primitive backing, stream ids or fixture values. Equal types do not prove equal runtime ids, and distinct concepts can hold equal values, so a finding is a likely modeling mistake rather than proof.
85
+ - It compares only where the producer is known: a command production with `for`, an implicit or generated command identifier, and a reaction production with an explicit `for`. All producers across the application count, after source and stream filters and production-route overrides.
86
+ - Not compared (unknown provenance): a plain `produces` without `for` (it lands on an identity the request allocates), a reaction without `for`, captures, seeds and code. An unknown producer suppresses the query comparison for that event.
87
+ - Collection queries, performer-served or unbuilt views, projection-backed views and public-event reducer targets do not trigger it.
88
+ - It is a native C# compiler diagnostic: the standalone tool, `cratis screenplay validate` (when its bundled compiler is new enough) and MCP diagnostics report it. The TypeScript compiler and editors only pass through findings the host supplies, so a clean editor is not evidence.
@@ -16,6 +16,7 @@
16
16
  { "kind": "skill-contains", "value": "**routes nothing** (`PLAY0381`)" },
17
17
  { "kind": "skill-contains", "value": "The key is **always the event source id**" },
18
18
  { "kind": "skill-contains", "value": "**A join never creates an" },
19
- { "kind": "skill-contains", "value": "Inline code uses a tagged fence" }
19
+ { "kind": "skill-contains", "value": "Inline code uses a tagged fence" },
20
+ { "kind": "skill-contains", "value": "PLAY0690", "file": "references/reducer-identity.md" }
20
21
  ]
21
22
  }
@@ -54,11 +54,13 @@ identity (all such queries must agree). Only with no single-instance identifier
54
54
  and an admitted list query does the binder infer an unambiguous `*Id`; queryless
55
55
  read models need an explicit `key`. See named keys below for collection-key exclusions.
56
56
  One explicit key binds byte-identically to equivalent inference. Several marks
57
- form a composite key, supplied by name in query/read
58
- `by` blocks. Composite keys and block queries remain
59
- **authoring-only** (PLAY0268); the scalar optional snapshot shape binds.
60
- Read [named keys](references/named-keys.md) for complete examples, trigger/command
61
- reads, all-parts fixtures, PLAY0660–0662 and command-read-only PLAY0661 repair.
57
+ form a flat composite key: an optional single-instance query names every part in a
58
+ `by` block, a list query narrows with equality filters, and fixtures state every part.
59
+ Requires Screenplay 4.204.0 (ESM v27; `screenplay --version`); older binders refuse
60
+ several key marks with PLAY0268. A list query over such a read model cannot use a
61
+ `by` block (PLAY0268).
62
+ Read [named keys](references/named-keys.md) for one complete executable example,
63
+ trigger/command reads, all-parts fixtures, PLAY0660–0662 and command-read-only PLAY0661 repair.
62
64
  A `=>` target resolving to an event is an event-target builder, not a read model;
63
65
  keep event and read-model names distinct (see projections' public-event targets).
64
66
 
@@ -78,7 +80,7 @@ query ListInvoices => InvoiceListReadModel[]
78
80
  **Name the compiler and renderer.** Optional keyed queries and collection queries
79
81
  bind, including `observable`. Caller-supplied equality
80
82
  filters on collections bind as ESM v14; `from`, single-result filters, `scoped to`,
81
- `performer` and composite `by` remain PLAY0268, each with its reason. Read
83
+ `performer` and a `by` block on a list query (or on a non-optional single result) remain PLAY0268, each with its reason. Read
82
84
  [equality filters](references/equality-filters.md) for the admitted shape, a complete
83
85
  compiled specification model and the Stage/CLI ceilings. Keep a list the domain needs.
84
86
 
@@ -177,13 +179,17 @@ primitive/concept/enum properties, same nominal type ignoring optionality, no
177
179
  `from`, distinct names not repeating `by`. `by` and supplied filters AND together;
178
180
  optional filters may be omitted, but supplied null is refused. PLAY0268 still
179
181
  rejects context sources, single-result filters, opaque scope/performer and
180
- composite `by`; a single non-optional result remains unadmitted too.
182
+ a `by` block on a list query (or on a non-optional single result).
181
183
  Authorization runs before lookup, ANDed with module/feature gates; policies see
182
184
  only `by`, never filters. Pin denial with `given caller` and `then denied`.
183
185
  Specs pass `by` and filters by name (PLAY0468 for unknown, repeated or missing
184
186
  required arguments); collection results match by read model/key regardless of
185
187
  row order. Details and renderer limits: [equality filters](references/equality-filters.md).
186
188
 
189
+ A reducer-built view is keyed by event-source id, so a single-instance `by` of another
190
+ nominal type warns `PLAY0690`. Rebuild it as a projection keyed on each `from`: projections
191
+ [reducer identity](../cratis-screenplay-projections/references/reducer-identity.md).
192
+
187
193
  ## `screen` — three levels
188
194
 
189
195
  The examples below are excerpts: each screen sits in a slice, and the queries,
@@ -301,6 +307,7 @@ binding failures are errors.
301
307
  - [ ] Every authorized query has a specification with `given caller`, including a
302
308
  `then denied` case.
303
309
  - [ ] Each read model has exactly one builder and every field traces to an event.
310
+ - [ ] A reducer-built view's `by` type matches its events' event-source identity (`PLAY0690`).
304
311
  - [ ] No screen reference is left ambiguous or unresolved.
305
312
  - [ ] Every UI binding that consumes screen data (component context, toolbar
306
313
  parameter, dialog input, outlet route) has a source in the screen contract
@@ -40,7 +40,7 @@ Each binding refusal reports PLAY0268 with its query/filter and reason:
40
40
  | Filter on a single-result query | Equality filters are admitted only for collections |
41
41
  | `scoped to` | The named scope has no portable executable scope contract |
42
42
  | `performer` | Query implementation code is opaque, not a portable declarative lookup |
43
- | Composite `by` block | Multipart lookup admission remains deferred (Screenplay#599) |
43
+ | `by` block on a collection query | A composite lookup is admitted only for an optional single-instance query; narrow a list by composite-keyed read-model parts with equality filters instead |
44
44
  | 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 |
45
45
 
46
46
  Keep those intents when the domain needs them; never drop a context source or