@cratis/pi 7.119.0 → 7.120.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.
@@ -98,16 +98,16 @@ if [ "$SELFTEST" = 1 ]; then
98
98
  chk "fresh worktree kept" '[ -d "$t/wt-fresh" ]'
99
99
  chk "fresh branch kept" 'git rev-parse -q --verify refs/heads/fresh >/dev/null'
100
100
  chk "main kept" 'git rev-parse -q --verify refs/heads/main >/dev/null'
101
- chk "report names the removal" 'printf "%s" "$out" | grep -q "removed worktree"'
101
+ chk "report names the removal" 'grep -q "removed worktree" <<<"$out"'
102
102
  chk "work records kept" '[ -f "$t/wt-records/.ai-work/.handoff" ]'
103
103
  chk "work records branch kept" 'git rev-parse -q --verify refs/heads/records >/dev/null'
104
- chk "work records reason reported" 'printf "%s" "$out" | grep -Fq "has untracked work records (.ai-work/)"'
104
+ chk "work records reason reported" 'grep -Fq "has untracked work records (.ai-work/)" <<<"$out"'
105
105
  chk "empty work records removed" '[ ! -d "$t/wt-empty-records" ]'
106
106
  chk "locked worktree kept" '[ -d "$t/wt-locked" ]'
107
107
  chk "locked branch kept" 'git rev-parse -q --verify refs/heads/locked >/dev/null'
108
- chk "locked reason reported" 'printf "%s" "$out" | grep -Fq "worktree $t/wt-locked (locked): locked"'
108
+ chk "locked reason reported" 'grep -Fq "worktree $t/wt-locked (locked): locked" <<<"$out"'
109
109
  chk "locked detached kept" '[ -d "$t/wt-locked-detached" ]'
110
- chk "locked detached reported" 'printf "%s" "$out" | grep -Fq "worktree $t/wt-locked-detached (detached): locked"'
110
+ chk "locked detached reported" 'grep -Fq "worktree $t/wt-locked-detached (detached): locked" <<<"$out"'
111
111
  out=$(printf '%s' '{"hook_event_name":"SessionStart"}' | "$self" --no-fetch --include-work-records --hook 2>&1) || { echo "FAIL: hook exit $?"; echo "$out"; exit 1; }
112
112
  chk "hook ignores opt-in" '[ -f "$t/wt-records/.ai-work/.handoff" ]'
113
113
  out=$("$self" --no-fetch --include-work-records 2>&1) || { echo "FAIL: opt-in exit $?"; echo "$out"; exit 1; }
@@ -32,7 +32,8 @@ while IFS= read -r slice; do
32
32
  done <<EOF
33
33
  $changed
34
34
  EOF
35
- if printf '%s\n' "$changed" | LC_ALL=C grep -Fxq "$anchor"; then
35
+ # A here-string, never `printf … | grep -q`: under `pipefail` the early-exiting `grep -q` can SIGPIPE `printf`.
36
+ if LC_ALL=C grep -Fxq "$anchor" <<<"$changed"; then
36
37
  spec_paths="$(printf '%s\n' "$paths" | CRATIS_SLICE="$slice/" awk '
37
38
  index($0, ENVIRON["CRATIS_SLICE"]) == 1 && /\/when_[^\/]+(\/|\.cs$)/ { print }
38
39
  ')"
@@ -270,9 +270,9 @@ AWK
270
270
 
271
271
  if [[ "$self_test" == 1 ]]; then
272
272
  flags="$(grep '^FLAG' "$tmp/out.txt" 2>/dev/null || true)"
273
- printf '%s\n' "$flags" | grep -q $'\tReactorSideEffect\t' \
273
+ grep -q $'\tReactorSideEffect\t' <<<"$flags" \
274
274
  || { printf 'ai-corpus type-references self-test: FAILED - the seeded ReactorSideEffect fabrication was not flagged\n' >&2; exit 1; }
275
- if printf '%s\n' "$flags" | grep -qE $'\t(EventForEventSourceId|IReactor|ReactorSideEffectFailure)\t'; then
275
+ if grep -qE $'\t(EventForEventSourceId|IReactor|ReactorSideEffectFailure)\t' <<<"$flags"; then
276
276
  printf 'ai-corpus type-references self-test: FAILED - a real Cratis type was flagged as unresolved\n' >&2; exit 1
277
277
  fi
278
278
  printf 'ai-corpus type-references self-test: passed - the seeded fabrication is named and the real types stay silent\n' >&2
@@ -171,6 +171,36 @@ A read model a performer composes needs no projection, and no event builds it.
171
171
  Specify it from state with `given readmodel`, then `when query <Query>` with the
172
172
  arguments and `then result` - see `cratis-screenplay-specifications`.
173
173
 
174
+ #### Performer bodies are rendered
175
+
176
+ Reach for a performer body when the declarative query cannot say what the read needs, for
177
+ example a filter relative to today: clock operands are syntax-only in query filters, so the
178
+ body computes it. Attach the body with `file` (or an inline `csharp` fence):
179
+
180
+ ```screenplay excerpt
181
+ query FiguresInRegion => InvoiceFigures[]
182
+ filter region String
183
+ filter status String optional
184
+ performer
185
+ file Queries/FiguresInRegion.cs
186
+ ```
187
+
188
+ The body is synchronous C# that returns the read-model instance (or null) for a single query,
189
+ or `IEnumerable<ReadModel>` for a collection. It sees `readModels` (the current collection), the
190
+ caller arguments by camelCase name and on `context.Arguments`, and `context.Tenant`,
191
+ `context.Identity` and `context.ReceivedAt`, for example
192
+ `return readModels.Where(figures => figures.Total > 0);`.
193
+
194
+ Stage renders the read model record and the query method with its route, parameters (filters in
195
+ declaration order, optional ones defaulted) and the modeled `[Authorize]` policy, and embeds the
196
+ verified body in a compile-checked helper; no projection or reducer is needed. Do not hand-write
197
+ a supported performer as gap-fill: attach the body to the model and render. An observable
198
+ performer, a non-C# body, a missing, changed or hash-invalid body, or a body that does not compile
199
+ becomes an explicit implementation gap instead, with a not-fully-runnable marker (see
200
+ [bodies and implementation gaps](../cratis-stage-rendering-and-sandbox/references/current-boundaries.md#bodies-and-implementation-gaps)).
201
+
202
+ Requires Stage 4.197.0 to render performer bodies, run directly or through a CLI that bundles it (check with `cratis --version`; CLI 3.100.0 bundles Stage 4.196.0); older versions keep the gap-fill path.
203
+
174
204
  A query's key goes on its own `by` line in the body. `query X => RM optional by id Type`
175
205
  on the header line is a declaration error, not a shorthand.
176
206
 
@@ -35,7 +35,7 @@ ESM). Only the probe answers renderability. `PLAY0270` entries are information,
35
35
  | Class | Meaning | Typical examples (each verified in `admission.md`) | Action |
36
36
  | --- | --- | --- | --- |
37
37
  | **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; two projections for one read model (`-007`); a policy Stage cannot express portably (`-015`; role-only and opaque pure policies render) | edit request to the modeler session with address, change and rationale |
38
- | **Capability gap** | the intended meaning needs a construct Stage does not render yet | DateTime equality filters and required parameters after optional filters (`-010`); context/single-result filters, opaque `scoped to` and a list-query `by` block remain PLAY0268; a query `performer` (opaque read, PLAY0270) is left out of the model so render emits nothing for it: write the code the model needs by hand as a gap-fill item; a read model with several key parts or a projection keyed by parts (v27) refuses `STAGE-ESM-048` and a single-instance query by parts `STAGE-ESM-047` (Chronicle#4265); Translate, clocks, captures, non-event triggers and opaque reaction bodies (`-024`); claim-reading/opaque `runs as` gates (`-034`), legacy `from` (`-035`), mixed effects in one handler (`-037`, retries can duplicate events; keep it whole), unsupported reaction mappings (`-039`); unsupported generated types/result combinations (`-028`/`-038`); `then no readmodel` (`-027`); evolved events (`STAGE-ESM-026`); unsupported expression/enum production conditions (`-006`; supported conditional productions render); legacy reads (`PLAY0271`), protected reads the renderer cannot preserve, or any protected read in an older CLI bundle (STAGE-ESM-044 despite v25 admission; semantic Host/executor support is separate); code validation (`-005`); protected identities/enums (`-040`) or unadmitted explicit subject lineage (`PLAY0268`); described handlers (v23, STAGE-IMPL-002 even Stub mode), atomic mode (STAGE-ESM-043; v22 admission Requires Stage 4.139.0, older Stage reports -016) | keep the model; fallback record entry; options a to c in `SKILL.md`; link the Stage issue |
38
+ | **Capability gap** | the intended meaning needs a construct Stage does not render yet | DateTime equality filters and required parameters after optional filters (`-010`); context/single-result filters, opaque `scoped to` and a list-query `by` block remain PLAY0268; an unsupported query performer (observable, a non-C# body, or a missing, changed or non-compiling body) becomes an explicit implementation gap, while a verified C# body renders ([performer bodies](../../cratis-screenplay-read-surface/SKILL.md#performer-bodies-are-rendered)); a read model with several key parts or a projection keyed by parts (v27) refuses `STAGE-ESM-048` and a single-instance query by parts `STAGE-ESM-047` (Chronicle#4265); Translate, clocks, captures, non-event triggers and opaque reaction bodies (`-024`); claim-reading/opaque `runs as` gates (`-034`), legacy `from` (`-035`), mixed effects in one handler (`-037`, retries can duplicate events; keep it whole), unsupported reaction mappings (`-039`); unsupported generated types/result combinations (`-028`/`-038`); `then no readmodel` (`-027`); evolved events (`STAGE-ESM-026`); unsupported expression/enum production conditions (`-006`; supported conditional productions render); legacy reads (`PLAY0271`), protected reads the renderer cannot preserve, or any protected read in an older CLI bundle (STAGE-ESM-044 despite v25 admission; semantic Host/executor support is separate); code validation (`-005`); protected identities/enums (`-040`) or unadmitted explicit subject lineage (`PLAY0268`); described handlers (v23, STAGE-IMPL-002 even Stub mode), atomic mode (STAGE-ESM-043; v22 admission Requires Stage 4.139.0, older Stage reports -016) | keep the model; fallback record entry; options a to c in `SKILL.md`; link the Stage issue |
39
39
  | **Implementation attachment** | a permanent limitation, not future ESM admission | file/inline command handlers (PLAY0800; typed `subject` names the command) | keep the model contract and gap-fill the slice in code with a constrained implementation attachment; do not wait for a new ESM version |
40
40
  | **Tool skew** | the bundled compiler disagrees with documented semantics | the false `PLAY0285` on reaction cascades from an older bundle; codes the standalone compiler does not raise | record in the session state and the fallback record; never "fix" a correct model |
41
41
  | **Environment** | not the model | restore failures, missing SDK, a destination that is not writable | fix the environment or report blocked |
@@ -120,7 +120,7 @@ Ask the batch below only for what the request leaves open: (1) origin of each ac
120
120
 
121
121
  - At most one `identifier` per command; never on an event property or read-model property (use `key`). At most one builder per read model (`PLAY0191`). Read `references/read-model-design.md` ("Identifier and key contract") for the exact rules.
122
122
 
123
- - A query's `by` goes on its own body line; `from`, single-result filters, scope and a list-query `by` block remain `PLAY0268` (a `performer` query is an opaque read left out of the executable model, [version gate](../cratis-screenplay-toolchain/references/executable-subset.md#code-attachments-what-binds-what-never-does)); equality filters on collections bind as ESM v14. Read `references/read-model-design.md` ("Query binding") for the binding rules.
123
+ - A query's `by` goes on its own body line; `from`, single-result filters, scope and a list-query `by` block remain `PLAY0268` (a `performer` query is an opaque read left out of the executable model, while Stage renders its verified C# body: [performer bodies](../cratis-screenplay-read-surface/SKILL.md#performer-bodies-are-rendered); [version gate](../cratis-screenplay-toolchain/references/executable-subset.md#code-attachments-what-binds-what-never-does)); equality filters on collections bind as ESM v14. Read `references/read-model-design.md` ("Query binding") for the binding rules.
124
124
 
125
125
  - A `unique` constraint names a property the event declares directly; the constraint name is
126
126
  its identity (renaming starts an empty index).
@@ -92,7 +92,7 @@ Requires Screenplay 4.141.0 or later (`screenplay --version`) to bind concept co
92
92
  equivalent inferred ESM bytes. Flat composite read-model keys bind as v27
93
93
  (see below). Collections and observable delivery bind.
94
94
  Caller-supplied equality filters on collections also bind as ESM v14; context
95
- sources, single-result filters, scope and a list-query `by` block remain blocked; a `performer` query is an opaque read left out of the executable model.
95
+ sources, single-result filters, scope and a list-query `by` block remain blocked; a `performer` query is an opaque read left out of the executable model (Stage still renders its verified C# body: [performer bodies](../../cratis-screenplay-read-surface/SKILL.md#performer-bodies-are-rendered)).
96
96
  - One projection per read model: `from`, `every`, `all` (one block per level), `join`,
97
97
  `children`, `nested`, `remove with`, `remove via join` (binds; blocks the execution plan at the projection level, see below), `clear with`, counters, `add`,
98
98
  `subtract`, `set`, `clear`, variants, and the identifier mapped from the projection's key (`xId = $eventSourceId` only for an event-source key).
@@ -331,7 +331,7 @@ code to an owner. Two kinds block binding (V3 blocked, no specification runs):
331
331
  the attached command `handler` (PLAY0800) and the `file` constraint (PLAY0268, see below).
332
332
  A query `performer` does not block: it is an opaque read, reported as information
333
333
  `PLAY0270` and left out of the executable model; a specification that reads it is left
334
- out, and every other specification still runs with `screenplay test`.
334
+ out, and every other specification still runs with `screenplay test`. That is the compiler fact only; Stage renders the body separately ([performer bodies](../../cratis-screenplay-read-surface/SKILL.md#performer-bodies-are-rendered)).
335
335
  Requires Screenplay 4.220.1 (`screenplay --version`); older versions refuse a performer
336
336
  query with PLAY0268 and leave the whole model unbound. Everything else binds as an **opaque requirement** that needs a target to
337
337
  execute: reducer transitions, rule predicates, command and concept code validation, policy
@@ -176,7 +176,7 @@ Requires Stage 4.90.0 for snapshot/live lists, 4.96.0 for non-text secrets and 4
176
176
  | `STAGE-ESM-005` | Code validation or rule predicate bodies; or a command beyond the first capability (optional event property, event revision other than the first, validation or requirement that cannot be rendered, neither `produces` nor a response) | Use declarative validation; non-initial event generations are not rendered |
177
177
  | `STAGE-ESM-006` | `produces` that cannot render without changing its destination or mappings (expression/enum conditions, literal or computed mappings, `for` not a command identifier, mappings not one-to-one with event properties, duplicate tags) | Map properties one to one; supported conditional productions render as guarded events, not separate commands |
178
178
  | `STAGE-ESM-007`, `-008`, `-009` | Projection count per read model (exactly one projection or reducer each); unresolvable properties or no single read-model transition (**flat** projections only); affected instance not one, not keyed by an event property, or not the event-source identity (flat projections only) | One projection per read model; see "Projections" below |
179
- | `STAGE-ESM-010` | A query is neither an optional snapshot lookup by the read-model identifier nor a supported snapshot/observable collection query; each query is checked on its own. Both collection modes render. Concept-typed keys and filters retain Arc validation, including composite members and collection elements; nullable concept keys or optional concept filters with `not empty` cannot preserve null semantics | Keyed `XById => X optional` queries or snapshot/observable lists. Equality filters render on both collections; DateTime filters, required filters after optional ones and unsupported null-validation shapes refuse 010. Filter-name collisions refuse 012. Context/single-result filters, opaque scope and a list-query `by` block remain PLAY0268; a performer query is left out of the model (PLAY0270, Requires Screenplay 4.220.1), so render emits nothing for it and it is a gap-fill item; unsupported composite-key projection shapes refuse `STAGE-ESM-048`; single-instance lookup by parts remains `-047` |
179
+ | `STAGE-ESM-010` | A query is neither an optional snapshot lookup by the read-model identifier nor a supported snapshot/observable collection query; each query is checked on its own. Both collection modes render. Concept-typed keys and filters retain Arc validation, including composite members and collection elements; nullable concept keys or optional concept filters with `not empty` cannot preserve null semantics | Keyed `XById => X optional` queries or snapshot/observable lists. Equality filters render on both collections; DateTime filters, required filters after optional ones and unsupported null-validation shapes refuse 010. Filter-name collisions refuse 012. Context/single-result filters, opaque scope and a list-query `by` block remain PLAY0268; a performer query is left out of the executable model (PLAY0270, Requires Screenplay 4.220.1) while Stage renders its verified C# body or reports an explicit gap ([bodies and implementation gaps](current-boundaries.md#bodies-and-implementation-gaps)); unsupported composite-key projection shapes refuse `STAGE-ESM-048`; single-instance lookup by parts remains `-047` |
180
180
  | `STAGE-ESM-011` | A specification cannot render (see the rules below) | Reshape it only if the assertion keeps its meaning; otherwise keep the specification, record the gap, and gap-fill with the specification as the oracle. Never weaken or delete it to get a render |
181
181
  | `STAGE-ESM-012` | Generated C# names collide (properties, types, namespaces, constraints) | Rename |
182
182
  | `STAGE-ESM-013` | An event-context value other than `$context.occurred` mapped in `produces` | Map a command property when that preserves the meaning; otherwise gap-fill |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cratis/pi",
3
- "version": "7.119.0",
3
+ "version": "7.120.0",
4
4
  "description": "Configuration-aware Cratis AI integration for Pi",
5
5
  "type": "module",
6
6
  "files": [