@cratis/pi 7.74.0 → 7.76.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.
- package/package/corpus/harnesses/copilot/instructions/vertical-slices.instructions.md +6 -4
- package/package/corpus/harnesses/pi/extensions/cratis-hooks/index.ts +5 -2
- package/package/corpus/hooks/README.md +27 -3
- package/package/corpus/hooks/scripts/cratis-quality-gate.sh +67 -4
- package/package/corpus/hooks/scripts/quality-gates.json +7 -1
- package/package/corpus/product-sync.json +14 -14
- package/package/corpus/rules/general.md +1 -0
- package/package/corpus/rules/vertical-slices.md +6 -4
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +9 -0
- package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/observable-emission-guards.md +12 -3
- package/package/corpus/skills/cratis-arc-command/references/decisions-and-routing.md +11 -0
- package/package/corpus/skills/cratis-arc-command-execution/SKILL.md +3 -0
- package/package/corpus/skills/cratis-arc-command-execution/references/occurrence-commands.md +78 -0
- package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +10 -36
- package/package/corpus/skills/cratis-arc-command-validation/references/input-binding.md +49 -0
- package/package/corpus/skills/cratis-arc-command-validation/references/read-model-resolution.md +25 -0
- package/package/corpus/skills/cratis-arc-command-validation/references/validation-severity.md +21 -0
- package/package/corpus/skills/cratis-arc-ef-core-migration/SKILL.md +7 -1
- package/package/corpus/skills/cratis-arc-hosting/SKILL.md +3 -0
- package/package/corpus/skills/cratis-arc-hosting/references/connection-resolution.md +61 -0
- package/package/corpus/skills/cratis-arc-query-paging/SKILL.md +4 -2
- package/package/corpus/skills/cratis-arc-react-page/references/queries-and-commands.md +14 -1
- package/package/corpus/skills/cratis-chronicle-client-dotnet/SKILL.md +5 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/immediate-append-refusal.md +40 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/lifecycle.md +4 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/occurrences.md +89 -0
- package/package/corpus/skills/cratis-chronicle-client-dotnet/references/pinned-generation-delivery.md +33 -3
- package/package/corpus/skills/cratis-chronicle-decision-reads/SKILL.md +5 -2
- package/package/corpus/skills/cratis-chronicle-decision-reads/references/admission-and-ownership.md +18 -1
- package/package/corpus/skills/cratis-chronicle-event-type-migration/references/pinned-generations.md +6 -0
- package/package/corpus/skills/cratis-chronicle-hosting/SKILL.md +3 -0
- package/package/corpus/skills/cratis-chronicle-hosting/references/operations.md +26 -0
- package/package/corpus/skills/cratis-chronicle-hosting/references/storage-resolution.md +55 -0
- package/package/corpus/skills/cratis-chronicle-multi-tenancy/SKILL.md +3 -0
- package/package/corpus/skills/cratis-chronicle-multi-tenancy/references/remove-namespace.md +38 -0
- package/package/corpus/skills/cratis-chronicle-projection/references/definition-changes.md +26 -0
- package/package/corpus/skills/cratis-chronicle-projection/references/model-bound-attributes.md +8 -0
- package/package/corpus/skills/cratis-chronicle-reactor/SKILL.md +5 -22
- package/package/corpus/skills/cratis-chronicle-reactor/references/starting-and-replaying.md +33 -0
- package/package/corpus/skills/cratis-chronicle-read-model/SKILL.md +9 -2
- package/package/corpus/skills/cratis-chronicle-read-model/references/queries.md +9 -4
- package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +4 -0
- package/package/corpus/skills/cratis-multi-service-architecture/SKILL.md +9 -3
- package/package/corpus/skills/cratis-performance-review/SKILL.md +4 -1
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/SKILL.md +2 -2
- package/package/corpus/skills/cratis-screenplay-automations-and-translations/references/realization-and-gap-fill.md +10 -2
- package/package/corpus/skills/cratis-screenplay-captures-and-reactions/SKILL.md +9 -9
- package/package/corpus/skills/cratis-screenplay-captures-and-reactions/references/atomic-batches-and-source-mappings.md +2 -2
- package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +11 -11
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-tools.md +9 -0
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/model-notes.md +41 -23
- package/package/corpus/skills/cratis-screenplay-model-authoring/verification.json +8 -3
- package/package/corpus/skills/cratis-screenplay-projections/SKILL.md +19 -12
- package/package/corpus/skills/cratis-screenplay-projections/references/pdl-grammar.md +37 -2
- package/package/corpus/skills/cratis-screenplay-projections/verification.json +11 -0
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/SKILL.md +37 -40
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/gap-fill-handoff.md +3 -2
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/references/renderability-gate.md +1 -1
- package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/verification.json +4 -2
- package/package/corpus/skills/cratis-screenplay-scenario-coverage/references/coverage-matrix.md +9 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +13 -30
- package/package/corpus/skills/cratis-screenplay-toolchain/references/diagnostics.md +39 -6
- package/package/corpus/skills/cratis-screenplay-toolchain/references/executable-subset.md +5 -2
- package/package/corpus/skills/cratis-screenplay-toolchain/references/language-admission.md +15 -5
- package/package/corpus/skills/cratis-screenplay-toolchain/references/renderable-subset.md +7 -4
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +12 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/verification.json +9 -2
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +38 -56
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +23 -13
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/date-comparisons.md +44 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/rendered-application.md +8 -2
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/sandbox-and-specrunner.md +59 -8
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/verification.json +7 -3
- package/package.json +1 -1
|
@@ -257,15 +257,17 @@ public record AuthorRegistered(AuthorName Name);
|
|
|
257
257
|
|
|
258
258
|
## Read Models and Queries
|
|
259
259
|
|
|
260
|
-
**[contract]** `[ReadModel]` records expose
|
|
260
|
+
**[contract]** `[ReadModel]` records expose eligible public and internal static methods as queries; internal visibility does not hide an endpoint. Dependencies are plain method parameters (no `[FromServices]`). For application code, start with model-bound mappings, use fluent when the definition needs structure, and choose reducer code when transitions read better as code. For step-by-step creation, invoke the **cratis-chronicle-read-model** skill.
|
|
261
261
|
|
|
262
262
|
Use `[ReadModel(RequireQueryAttribute = true)]` with `[Query]` on intended
|
|
263
|
-
endpoints when
|
|
264
|
-
|
|
263
|
+
endpoints when folds exist (ARC0026/ARCCHR0019). `[Query]` alone does not hide
|
|
264
|
+
siblings. Keep helpers outside the read model regardless of visibility.
|
|
265
|
+
Requires Arc 22.27.1 for private/protected/compiler-generated methods to be
|
|
266
|
+
excluded; older versions can expose private statics too. Check `dotnet list package`.
|
|
265
267
|
A `CancellationToken` is supplied by Arc; concept arguments come from the caller,
|
|
266
268
|
not DI. Bad GET/QUERY argument conversions return 400. Single-instance MongoDB/EF
|
|
267
269
|
observations emit `default` on absence/deletion/filter exit: guard `hasData` and
|
|
268
|
-
`isReady` in React
|
|
270
|
+
`isReady` in React; see the read-model query reference.
|
|
269
271
|
|
|
270
272
|
### Query return shapes **[contract]**
|
|
271
273
|
|
|
@@ -332,7 +332,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
332
332
|
label: "Cratis quality gate",
|
|
333
333
|
description:
|
|
334
334
|
"Run the Cratis quality gate (.cratis/ai/hooks/scripts/cratis-quality-gate.sh): the build, spec and lint gates the current " +
|
|
335
|
-
"working-tree changes touch
|
|
335
|
+
"working-tree changes touch. Shipped defaults: .cratis/ai/hooks/scripts/quality-gates.json; project overrides: " +
|
|
336
|
+
".cratis/ai/quality-gates.project.json, shallow-merged by gate id (command without requires drops inherited packageScripts). " +
|
|
337
|
+
"CRATIS_HOOKS_GATES replaces the defaults; project overrides still merge over it. " +
|
|
338
|
+
"Runs in the foreground with a whole-tool deadline and optional per-gate timeoutSeconds; can be cancelled. " +
|
|
336
339
|
"A failure, a timeout or a cancellation is returned as an error and means the change is NOT verified. " +
|
|
337
340
|
"Run it explicitly at a verification checkpoint, not after every prompt. It selects gates for all current " +
|
|
338
341
|
"working-tree changes, not the complete CI matrix; run CI-equivalent gates before claiming completion or pushing/opening a PR. " +
|
|
@@ -378,7 +381,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
378
381
|
const last = latestGateLog(gateLogDirectory(sessionId), started);
|
|
379
382
|
const tail = last ? tailLines(last.file, TAIL_LINES) : "";
|
|
380
383
|
throw new Error(`The Cratis quality gate TIMED OUT after ${duration(timeoutSeconds * 1000)}${last ? ` while running '${last.gate}'${last.cwd ? ` (cwd: ${last.cwd})` : ""}` : ""}. This is not a pass: nothing was verified.` +
|
|
381
|
-
(last ? `\n\nGate log: ${last.file}` : "") +
|
|
384
|
+
(last ? `\nRun a narrower command instead: ${last.gate === "backend-specs" ? "dotnet test <affected.Specs.csproj-or-Tests.csproj> --configuration Debug --no-build (replace the placeholder; repeat for affected dependents)." : "run the affected project alone using this gate's configured command."}\nConfigure it in .cratis/ai/quality-gates.project.json.\n\nGate log: ${last.file}` : "") +
|
|
382
385
|
(tail ? `\n\n--- last ${TAIL_LINES} lines ---\n${tail}\n--- end ---` : ""));
|
|
383
386
|
};
|
|
384
387
|
const reportInterrupted = () => {
|
|
@@ -277,6 +277,9 @@ their explicit consent. Slice-only scope checks do not replace the repository's
|
|
|
277
277
|
|
|
278
278
|
### Project-specific gate configuration
|
|
279
279
|
|
|
280
|
+
Shipped defaults live in `.cratis/ai/hooks/scripts/quality-gates.json`, not `.cratis/ai/quality-gates.json`.
|
|
281
|
+
Project overrides live in `.cratis/ai/quality-gates.project.json`. `CRATIS_HOOKS_GATES=<path>` replaces
|
|
282
|
+
the defaults file; the project overrides still merge over that replacement by gate id.
|
|
280
283
|
The shipped gates discover the repository's own solution and package, so most repositories need no
|
|
281
284
|
configuration at all. An eligible root workspace or solution keeps repository-wide dispatch. Without
|
|
282
285
|
one, discovery uses only each gate's matching, non-excluded changes and runs it once per affected
|
|
@@ -547,9 +550,30 @@ inapplicable gate remains a no-op. Pi preserves the script's diagnostics when pl
|
|
|
547
550
|
The same shipped file therefore activates in application and framework repositories, and reports
|
|
548
551
|
an empty plan without running gates in a repository that has no project at all.
|
|
549
552
|
|
|
550
|
-
**Overriding it
|
|
551
|
-
several candidate projects
|
|
552
|
-
|
|
553
|
+
**Overriding it.** Set `workingDirectory` on a gate in `.cratis/ai/quality-gates.project.json` to pin
|
|
554
|
+
one of several candidate projects. For a replacement defaults file, point `CRATIS_HOOKS_GATES` at a
|
|
555
|
+
file anywhere instead of editing the managed file. Neither requires forking the script.
|
|
556
|
+
A missing defaults file reports both real configuration paths and runs no gates.
|
|
557
|
+
|
|
558
|
+
**Per-gate deadlines.** `timeoutSeconds` is an optional integer from 1 to 86400, applied separately
|
|
559
|
+
to each command/directory. Unset means no per-gate deadline; Pi's whole-tool deadline still applies
|
|
560
|
+
and may expire first. The shipped `backend-specs` gate allows 120 seconds so a long solution-wide
|
|
561
|
+
spec run stops with a named diagnostic rather than consuming the entire tool budget. On timeout,
|
|
562
|
+
the script stops the command's process group (TERM, then KILL after one second), exits **2**, and
|
|
563
|
+
reports **NOT VERIFIED**, the gate, its directory, log tail, and `timeoutHint` (advice, never executed).
|
|
564
|
+
Commands that create separate sessions/process groups can escape this cleanup; this is not a sandbox.
|
|
565
|
+
|
|
566
|
+
For a large solution, replace the placeholder below with the affected test project and repeat for
|
|
567
|
+
its affected dependents. Override `command` with that narrower argv array (or a repository-owned
|
|
568
|
+
runner covering all affected projects), rather than raising the timeout without diagnosing the delay:
|
|
569
|
+
|
|
570
|
+
```json
|
|
571
|
+
{ "gates": [{ "id": "backend-specs", "command": ["dotnet", "test", "<affected.Specs.csproj>", "--configuration", "Debug", "--no-build"], "timeoutSeconds": 120, "timeoutHint": "Run dotnet test for each affected Specs/Tests project separately." }] }
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
This is not automatic affected-project selection: imported/conditional MSBuild references and
|
|
575
|
+
linked or shared sources require evaluation to establish coverage safely. The default retains
|
|
576
|
+
solution-wide tests rather than silently missing dependents, and a timeout is never a pass.
|
|
553
577
|
|
|
554
578
|
**Profile note.** The C# patterns are application-profile and scoped to `Source/**/*.cs`, which is
|
|
555
579
|
the application source root [`../rules/general.md`](../rules/general.md) documents. A
|
|
@@ -35,7 +35,10 @@ here="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
|
35
35
|
config="${CRATIS_HOOKS_GATES:-$here/quality-gates.json}"
|
|
36
36
|
dryrun="${CRATIS_HOOKS_GATE_DRYRUN:-0}"
|
|
37
37
|
|
|
38
|
-
[ -f "$config" ] ||
|
|
38
|
+
[ -f "$config" ] || {
|
|
39
|
+
printf 'cratis-quality-gate: configuration not found: %s — no gates ran. Shipped defaults: .cratis/ai/hooks/scripts/quality-gates.json; project overrides: .cratis/ai/quality-gates.project.json (merged by gate id). CRATIS_HOOKS_GATES replaces the defaults.\n' "$config" >&2
|
|
40
|
+
exit 0
|
|
41
|
+
}
|
|
39
42
|
jq -e . "$config" >/dev/null 2>&1 || {
|
|
40
43
|
printf 'cratis-quality-gate: %s is not valid JSON — gate skipped.\n' "$config" >&2
|
|
41
44
|
exit 0
|
|
@@ -265,7 +268,8 @@ discover_workdirs() {
|
|
|
265
268
|
|
|
266
269
|
gate_count="$(jq -r '.gates | length' "$config")"
|
|
267
270
|
[ "${gate_count:-0}" -gt 0 ] || exit 0
|
|
268
|
-
|
|
271
|
+
# jq's // treats false as absent; failFast=false must keep running after a failed gate.
|
|
272
|
+
fail_fast="$(jq -r '.failFast | if . == null then true else . end' "$config")"
|
|
269
273
|
max_lines="$(jq -r '.maxOutputLines // 60' "$config")"
|
|
270
274
|
|
|
271
275
|
tmp_root="$(hook_state_dir "$(hook_json "$input" '.session_id')")" || tmp_root="${TMPDIR:-/tmp}"
|
|
@@ -335,6 +339,46 @@ EOF
|
|
|
335
339
|
return 0
|
|
336
340
|
}
|
|
337
341
|
|
|
342
|
+
# Bash job control gives the command and watchdog separate process groups without GNU timeout
|
|
343
|
+
# or a language runtime. Keep the supervisor in the caller's group so Pi cancellation reaches
|
|
344
|
+
# its trap too. Wait for escalation even if the command leader exits on TERM: its test hosts
|
|
345
|
+
# may still be running. Only configured gates use this wrapper; other gates remain unchanged.
|
|
346
|
+
run_timed_gate() (
|
|
347
|
+
set -m
|
|
348
|
+
local worker timer rc=0 marker="$log.timeout"
|
|
349
|
+
rm -f "$marker"
|
|
350
|
+
stop_group() {
|
|
351
|
+
kill -TERM -- "-$1" 2>/dev/null || return 0
|
|
352
|
+
sleep 1
|
|
353
|
+
kill -KILL -- "-$1" 2>/dev/null || true
|
|
354
|
+
}
|
|
355
|
+
cleanup() {
|
|
356
|
+
trap '' TERM INT
|
|
357
|
+
kill -TERM -- "-$timer" 2>/dev/null || true
|
|
358
|
+
stop_group "$worker"
|
|
359
|
+
wait "$timer" 2>/dev/null || true
|
|
360
|
+
wait "$worker" 2>/dev/null || true
|
|
361
|
+
}
|
|
362
|
+
(cd -P "$root/$wd" && exec "${cmd[@]}") </dev/null &
|
|
363
|
+
worker=$!
|
|
364
|
+
(
|
|
365
|
+
sleep "$gate_timeout"
|
|
366
|
+
printf 'timed out\n' >"$marker"
|
|
367
|
+
stop_group "$worker"
|
|
368
|
+
) &
|
|
369
|
+
timer=$!
|
|
370
|
+
trap 'cleanup; exit 2' TERM INT
|
|
371
|
+
wait "$worker" || rc=$?
|
|
372
|
+
if [ -f "$marker" ]; then
|
|
373
|
+
wait "$timer" || true
|
|
374
|
+
rc=124
|
|
375
|
+
else
|
|
376
|
+
cleanup
|
|
377
|
+
fi
|
|
378
|
+
trap - TERM INT
|
|
379
|
+
exit "$rc"
|
|
380
|
+
)
|
|
381
|
+
|
|
338
382
|
idx=0
|
|
339
383
|
ran=0
|
|
340
384
|
planned_gates=0
|
|
@@ -383,6 +427,15 @@ while [ "$idx" -lt "$gate_count" ]; do
|
|
|
383
427
|
$(jq -r --argjson i "$idx" '.gates[$i].command // [] | .[]' "$config")
|
|
384
428
|
EOF
|
|
385
429
|
|
|
430
|
+
gate_timeout="$(jq -r --argjson i "$idx" 'if .gates[$i].timeoutSeconds == null then "" else .gates[$i].timeoutSeconds end' "$config")"
|
|
431
|
+
timeout_hint="$(jq -r --argjson i "$idx" '.gates[$i].timeoutHint // empty' "$config")"
|
|
432
|
+
if ! jq -e --argjson i "$idx" '
|
|
433
|
+
.gates[$i].timeoutSeconds | . == null or (type == "number" and . >= 1 and . <= 86400 and floor == .)
|
|
434
|
+
' "$config" >/dev/null; then
|
|
435
|
+
printf 'cratis-quality-gate: UNVERIFIED %s — timeoutSeconds must be an integer from 1 to 86400.\n' "$id" >&2
|
|
436
|
+
exit 2
|
|
437
|
+
fi
|
|
438
|
+
|
|
386
439
|
project_index=0
|
|
387
440
|
gate_ran=0
|
|
388
441
|
gate_noops=0
|
|
@@ -424,13 +477,23 @@ EOF
|
|
|
424
477
|
rc=0
|
|
425
478
|
{
|
|
426
479
|
printf 'cratis-quality-gate: LOG %s (cwd: %s)\n' "$id" "$wd"
|
|
427
|
-
|
|
480
|
+
if [ -n "$gate_timeout" ]; then
|
|
481
|
+
run_timed_gate
|
|
482
|
+
else
|
|
483
|
+
(cd -P "$root/$wd" && "${cmd[@]}") </dev/null
|
|
484
|
+
fi
|
|
428
485
|
} >"$log" 2>&1 || rc=$?
|
|
429
486
|
ran=$((ran + 1))
|
|
430
487
|
|
|
431
488
|
if [ "$rc" -ne 0 ]; then
|
|
432
489
|
{
|
|
433
|
-
|
|
490
|
+
if [ -n "$gate_timeout" ] && [ -f "$log.timeout" ]; then
|
|
491
|
+
printf 'QUALITY GATE TIMED OUT: %s after %ss (cwd: %s) — NOT VERIFIED.\n' "$id" "$gate_timeout" "$wd"
|
|
492
|
+
printf 'Run a narrower command instead: %s\n' "${timeout_hint:-run the affected project with the command below, not the whole workspace/solution.}"
|
|
493
|
+
printf 'Configure command/workingDirectory/timeoutSeconds in .cratis/ai/quality-gates.project.json.\n'
|
|
494
|
+
else
|
|
495
|
+
printf 'QUALITY GATE FAILED: %s (exit %s)\n' "$id" "$rc"
|
|
496
|
+
fi
|
|
434
497
|
printf ' %s\n' "$desc"
|
|
435
498
|
printf ' $ %s (cwd: %s)\n\n' "${cmd[*]}" "$wd"
|
|
436
499
|
printf -- '--- last %s lines ---\n' "$max_lines"
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": [
|
|
3
3
|
"Quality gates run by the Stop hook (cratis-quality-gate.sh).",
|
|
4
|
-
"
|
|
4
|
+
"Shipped defaults: .cratis/ai/hooks/scripts/quality-gates.json; project overrides: .cratis/ai/quality-gates.project.json.",
|
|
5
|
+
"Data, not code: a consuming repository patches gates by id (or points CRATIS_HOOKS_GATES at",
|
|
5
6
|
"its own) without touching the script. Gates run in order and stop at the first failure.",
|
|
6
7
|
"",
|
|
7
8
|
"A gate runs only when the working tree contains a change matching one of its \"changed\" globs,",
|
|
@@ -41,6 +42,9 @@
|
|
|
41
42
|
" Mixed RUN/NO-OP directories report UNVERIFIED and exit 2, not a partial success.",
|
|
42
43
|
" Gate commands read /dev/null so they cannot consume remaining directories.",
|
|
43
44
|
" command argv array \u2014 executed directly, never through a shell (no eval)",
|
|
45
|
+
" timeoutSeconds optional integer 1..86400; deadline per command/directory, plus 1s TERM-to-KILL cleanup",
|
|
46
|
+
" (Pi's whole-tool deadline still applies); timeout exits 2, never a pass",
|
|
47
|
+
" timeoutHint narrower command to run when the gate times out; diagnostic text, never executed",
|
|
44
48
|
"",
|
|
45
49
|
"Managed corpus and adapter trees (.cratis/**, .pi/**, .claude/**) never count as a relevant change:",
|
|
46
50
|
"a corpus update must not run a repository's application gates over files that are not its application.",
|
|
@@ -114,6 +118,8 @@
|
|
|
114
118
|
{
|
|
115
119
|
"id": "backend-specs",
|
|
116
120
|
"description": "dotnet test (Debug, --no-build) \u2014 zero failures",
|
|
121
|
+
"timeoutSeconds": 120,
|
|
122
|
+
"timeoutHint": "dotnet test <affected.Specs.csproj-or-Tests.csproj> --configuration Debug --no-build (replace the placeholder with the affected test project; repeat for its affected dependents)",
|
|
117
123
|
"changed": [
|
|
118
124
|
"**/*.cs",
|
|
119
125
|
"**/*.csproj",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"repository": "Cratis/Stage",
|
|
28
28
|
"packages": [],
|
|
29
29
|
"synced": {
|
|
30
|
-
"version": "4.
|
|
30
|
+
"version": "4.146.0",
|
|
31
31
|
"date": "2026-10-17",
|
|
32
|
-
"by": "Cratis/AI#
|
|
32
|
+
"by": "Cratis/AI#773"
|
|
33
33
|
},
|
|
34
34
|
"covers": {
|
|
35
35
|
"skills": [
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
],
|
|
47
47
|
"rules": []
|
|
48
48
|
},
|
|
49
|
-
"notes": "
|
|
49
|
+
"notes": "Cratis/AI#773: release/tag delta v4.139.0-v4.146.0 (including v4.141.1). Exact ESM v1-v27 pairs admitted; v22 from 4.139.0, atomic mode still -043. Date comparisons render from 4.141.0 with remaining -046 shapes and named-policy binding differences. 4.141.1 supplies authored occurrence in independent event-reaction specs, not clock triggers or generated append/redelivery clock fixtures. 4.142.0 Host decide/commit absorbs unrelated outside facts, with dependency conflicts, guarded batch retries only without invocations and fail-closed unknown writes. 4.143.0 shared STAGE-PROFILE-001 through -006 findings; 4.144.0 scaffold pins follow Stage package inputs. 4.145.0 reached reducer/validation bodies use STAGE-IMPL-003 without refusing unrelated behavior; described handlers retain -002. 4.146.0 collects independent render refusals, preserving profile/version early exits. Scene template verification changes classified as internal; no new render/browser/Docker probe. Prior focused checks remain; v4.104-v4.108 were not otherwise fully reviewed."
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
"id": "arc",
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
}
|
|
64
64
|
],
|
|
65
65
|
"synced": {
|
|
66
|
-
"version": "22.
|
|
67
|
-
"date": "2026-10-
|
|
68
|
-
"by": "Cratis/AI#
|
|
66
|
+
"version": "22.117.0",
|
|
67
|
+
"date": "2026-10-17",
|
|
68
|
+
"by": "Cratis/AI#773"
|
|
69
69
|
},
|
|
70
70
|
"covers": {
|
|
71
71
|
"skills": [
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"vertical-slices.md"
|
|
100
100
|
]
|
|
101
101
|
},
|
|
102
|
-
"notes": "
|
|
102
|
+
"notes": "Reviewed releases 22.100.1-22.117.0 at tags: occurrence command receipts and retry classification, tenant/purpose storage resolution, explicit-offset binding and authorization of malformed bodies, camelCase validation members, conflict event details, hub fallback authorization and snapshot guards, and React query-key isolation/resubscription. Existing read-protection and generated-value guidance retained. Query discovery includes internal statics; public-only discovery in Arc#3368 is unreleased. Screenplay-generator deltas classified for the owning lane, not changed here. No new runtime probe."
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
105
|
"id": "ai",
|
|
@@ -171,9 +171,9 @@
|
|
|
171
171
|
}
|
|
172
172
|
],
|
|
173
173
|
"synced": {
|
|
174
|
-
"version": "4.
|
|
174
|
+
"version": "4.212.4",
|
|
175
175
|
"date": "2026-10-17",
|
|
176
|
-
"by": "Cratis/AI#
|
|
176
|
+
"by": "Cratis/AI#772"
|
|
177
177
|
},
|
|
178
178
|
"covers": {
|
|
179
179
|
"skills": [
|
|
@@ -201,7 +201,7 @@
|
|
|
201
201
|
],
|
|
202
202
|
"rules": []
|
|
203
203
|
},
|
|
204
|
-
"notes": "Cratis/AI#762 and #761: source-checked v4.210.0 and v4.211.0 contracts for PLAY0800 CommandHandlerRequiresAttachment (required typed subject, permanent code-attachment requirement, other PLAY0268 families unchanged), independent reference remove/add batches, located same-text ambiguity conflicts and string MCP conflict/move-report document fields. v4.211.1 mapping changes inspected but excluded from this guidance update at the user's request; no typed PLAY0273 property/type arguments are claimed. Standalone Screenplay 4.211.1 and its compiler contract pinned; the contract is a byte-identical copy of the v4.211.1 release asset. No new render/runtime probe. Prior: Standalone 4.205.0 and byte-identical compiler contract pinned. Cratis/AI#746: Uuid ==/!= in command require and produces when (ESM v28): same-concept rule, bare Uuid against a concept, canonical literals, protected read paths in require only; PLAY0268 (different concepts, ordering, Uuid validation and concept rules, read paths in produces when) and PLAY0273 (non-canonical literal) checked against the 4.205.0 compiler, v28 selection read from the MCP executable-model view; Stage admits exact v1-v21 and v23-v26 pairs so v28 is refused STAGE-ESM-016, not probed against a Stage image; CLI 3.74.0 renders through v26. Prior: 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."
|
|
204
|
+
"notes": "Cratis/AI#772: standalone Screenplay 4.212.4 pinned; regenerated compiler contract is byte-identical to the v4.212.4 release asset. Source-checked joined-event automap/no automap overrides and level fallback in executable binding; PLAY0380 covers only all inside children/nested. No new render/runtime probe. Prior: Cratis/AI#769: standalone Screenplay 4.212.3 pinned; regenerated compiler contract is byte-identical to the v4.212.3 release asset. Source-checked both compilers and projection documentation for PLAY0801 DuplicateEventInProjectionScope: shared from/removal/clear set, separate join set, fresh sets per children/nested/variant level, and no two-row update through repeated from blocks with different keys. No new render/runtime probe. Prior: Cratis/AI#768: standalone Screenplay 4.212.2 pinned; regenerated compiler contract is byte-identical to the v4.212.2 release asset (unchanged from the previous contract). Source-checked v4.212.0 split-model dotted note targets, open-workspace applicationIdentity and complete add/reanchor substitutions; unbound container identities are not claimed. v4.212.1 aligns PLAY0535 collection fields, optional parameters and single-result identity inference. v4.212.2 counts read-only query denials for SPEC004, not SPEC006. No new render/runtime probe. Prior: Cratis/AI#762 and #761: source-checked v4.210.0 and v4.211.0 contracts for PLAY0800 CommandHandlerRequiresAttachment (required typed subject, permanent code-attachment requirement, other PLAY0268 families unchanged), independent reference remove/add batches, located same-text ambiguity conflicts and string MCP conflict/move-report document fields. v4.211.1 mapping changes inspected but excluded from this guidance update at the user's request; no typed PLAY0273 property/type arguments are claimed. Standalone Screenplay 4.211.1 and its compiler contract pinned; the contract is a byte-identical copy of the v4.211.1 release asset. No new render/runtime probe. Prior: Standalone 4.205.0 and byte-identical compiler contract pinned. Cratis/AI#746: Uuid ==/!= in command require and produces when (ESM v28): same-concept rule, bare Uuid against a concept, canonical literals, protected read paths in require only; PLAY0268 (different concepts, ordering, Uuid validation and concept rules, read paths in produces when) and PLAY0273 (non-canonical literal) checked against the 4.205.0 compiler, v28 selection read from the MCP executable-model view; Stage admits exact v1-v21 and v23-v26 pairs so v28 is refused STAGE-ESM-016, not probed against a Stage image; CLI 3.74.0 renders through v26. Prior: 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",
|
|
@@ -284,9 +284,9 @@
|
|
|
284
284
|
}
|
|
285
285
|
],
|
|
286
286
|
"synced": {
|
|
287
|
-
"version": "19.
|
|
288
|
-
"date": "2026-10-
|
|
289
|
-
"by": "Cratis/AI#
|
|
287
|
+
"version": "19.81.1",
|
|
288
|
+
"date": "2026-10-17",
|
|
289
|
+
"by": "Cratis/AI#773"
|
|
290
290
|
},
|
|
291
291
|
"covers": {
|
|
292
292
|
"skills": [
|
|
@@ -323,7 +323,7 @@
|
|
|
323
323
|
],
|
|
324
324
|
"rules": []
|
|
325
325
|
},
|
|
326
|
-
"notes": "
|
|
326
|
+
"notes": "Reviewed releases 19.65.1-19.81.1 at tags: subscription generation pins, StartAtTail, namespace removal/reconnect, storage connection resolution (deploy 19.74.2+), definition refusal recovery, conflict event fields, .NET occurrences/lifecycle/receipts and testing identities, full-history per-key reads, child all-event mapping repair, projection SoftwareVersion, bounded job acceptance/recovery, immediate-append refusal ownership and namespace subscription reconciliation. Existing protected-read guidance retained. Chronicle#5028 definition round-trip/restart-replay fix is unreleased and not taught as fixed. No new kernel/transport probe."
|
|
327
327
|
},
|
|
328
328
|
{
|
|
329
329
|
"id": "arc-go",
|
|
@@ -95,6 +95,7 @@ A claim is only as good as the signal behind it — a build result, a test run,
|
|
|
95
95
|
|
|
96
96
|
- **Confirm "done"/"fixed"/"correct" against a fresh signal — never self-assessment.** Run the relevant gate and observe it pass *this time*.
|
|
97
97
|
- **Check at the right frequency.** Run affected-project checks while iterating; a prompt ending is not task completion. Before claiming a task complete or pushing/opening a PR, run the full CI-equivalent gates required for that repository (including its Tier 1/2 policy where applicable). A change-selected hook gate is not a substitute for the repository's full CI matrix. After a fix, re-run the gate that failed. Don't argue yourself to green.
|
|
98
|
+
- **Gate configuration:** shipped defaults `.cratis/ai/hooks/scripts/quality-gates.json`; project overrides `.cratis/ai/quality-gates.project.json` shallow-merge by gate id (command without requires drops inherited packageScripts). `CRATIS_HOOKS_GATES` replaces defaults; project overrides still apply.
|
|
98
99
|
- **A green build is not behavioral correctness.** Compilation proves it builds, not that the slice does the right thing — that's what specs and exercising the UI are for.
|
|
99
100
|
- **Report the conclusion and what you didn't verify, in a line or two.** Show the output when asked, when a claim is contested, or when the check failed — see [verification-discipline.md](./verification-discipline.md).
|
|
100
101
|
|
|
@@ -256,15 +256,17 @@ public record AuthorRegistered(AuthorName Name);
|
|
|
256
256
|
|
|
257
257
|
## Read Models and Queries
|
|
258
258
|
|
|
259
|
-
**[contract]** `[ReadModel]` records expose
|
|
259
|
+
**[contract]** `[ReadModel]` records expose eligible public and internal static methods as queries; internal visibility does not hide an endpoint. Dependencies are plain method parameters (no `[FromServices]`). For application code, start with model-bound mappings, use fluent when the definition needs structure, and choose reducer code when transitions read better as code. For step-by-step creation, invoke the **cratis-chronicle-read-model** skill.
|
|
260
260
|
|
|
261
261
|
Use `[ReadModel(RequireQueryAttribute = true)]` with `[Query]` on intended
|
|
262
|
-
endpoints when
|
|
263
|
-
|
|
262
|
+
endpoints when folds exist (ARC0026/ARCCHR0019). `[Query]` alone does not hide
|
|
263
|
+
siblings. Keep helpers outside the read model regardless of visibility.
|
|
264
|
+
Requires Arc 22.27.1 for private/protected/compiler-generated methods to be
|
|
265
|
+
excluded; older versions can expose private statics too. Check `dotnet list package`.
|
|
264
266
|
A `CancellationToken` is supplied by Arc; concept arguments come from the caller,
|
|
265
267
|
not DI. Bad GET/QUERY argument conversions return 400. Single-instance MongoDB/EF
|
|
266
268
|
observations emit `default` on absence/deletion/filter exit: guard `hasData` and
|
|
267
|
-
`isReady` in React
|
|
269
|
+
`isReady` in React; see the read-model query reference.
|
|
268
270
|
|
|
269
271
|
### Query return shapes **[contract]**
|
|
270
272
|
|
|
@@ -217,6 +217,15 @@ MVC actions). Configure both: the evaluator for the Arc pipeline, `FallbackPolic
|
|
|
217
217
|
for MVC. Neither protects an endpoint that is mapped explicitly anonymous (see the
|
|
218
218
|
discovery endpoints in the skill).
|
|
219
219
|
|
|
220
|
+
Requires Arc 22.116.0 for hub subscriptions to apply the host's endpoint fallback
|
|
221
|
+
before running an unannotated query: ASP.NET Core evaluates `FallbackPolicy`,
|
|
222
|
+
and Arc Core requires authentication when authentication handlers are registered.
|
|
223
|
+
`IEndpointFallbackAuthorization` supplies this host check; it is separate from
|
|
224
|
+
`IFallbackAuthorizationEvaluator`. `[AllowAnonymous]` and guest-policy queries
|
|
225
|
+
remain exempt. Denial sends an Unauthorized frame; SSE subscribe returns 401.
|
|
226
|
+
Check with `dotnet list package`; on older Arc do not rely on host fallback alone
|
|
227
|
+
to secure the hub path.
|
|
228
|
+
|
|
220
229
|
## Default access by hosting model
|
|
221
230
|
|
|
222
231
|
There is no `ArcOptions` switch for a default requirement. What you get depends
|
|
@@ -44,7 +44,15 @@ Arc API, and never an always-true stub.
|
|
|
44
44
|
It runs on **every emission** of **every subscription**, on **every transport**:
|
|
45
45
|
the multiplexed hub, WebSocket and SSE.
|
|
46
46
|
|
|
47
|
-
Requires Arc 22.69.1 for guards and read-model interceptors on plain HTTP snapshots
|
|
47
|
+
Requires Arc 22.69.1 for guards and read-model interceptors on plain HTTP snapshots
|
|
48
|
+
of observable queries, waits and long polls; on an older Arc those requests are not guarded.
|
|
49
|
+
|
|
50
|
+
Requires Arc 22.116.0 for **non-observable** query results performed through the
|
|
51
|
+
hub to run emission guards. Such a result has `IsSnapshot = true` and
|
|
52
|
+
`IsFirstEmission = true`. `Suppress` withholds the result (SSE subscribe still
|
|
53
|
+
returns 200); denial sends Unauthorized (SSE subscribe returns 401). Ordinary
|
|
54
|
+
non-observable direct HTTP queries do not run these guards. Check with
|
|
55
|
+
`dotnet list package`; on older Arc secure hub snapshots without relying on a guard.
|
|
48
56
|
|
|
49
57
|
Source: [ObservableQueryHttp.cs](https://github.com/Cratis/Arc/blob/main/Source/DotNET/Arc.Core/Queries/ObservableQueryHttp.cs).
|
|
50
58
|
|
|
@@ -63,7 +71,7 @@ Several guards: every guard is asked, the **most restrictive** verdict wins
|
|
|
63
71
|
## The context
|
|
64
72
|
|
|
65
73
|
`ObservableQueryEmissionContext` carries `QueryName`, `Arguments`,
|
|
66
|
-
`SubscriptionScope`, `Principal`, `CorrelationId`, `IsFirstEmission`,
|
|
74
|
+
`SubscriptionScope`, `Principal`, `CorrelationId`, `IsFirstEmission`, `IsSnapshot`,
|
|
67
75
|
`CancellationToken` and the `ServiceProvider` to resolve from. Each guard gets its
|
|
68
76
|
own copy of the scope and arguments.
|
|
69
77
|
|
|
@@ -108,7 +116,8 @@ a failure.
|
|
|
108
116
|
|
|
109
117
|
## What a guard does not protect
|
|
110
118
|
|
|
111
|
-
- Plain HTTP **
|
|
119
|
+
- Plain HTTP **observable-query** snapshots are guarded (Requires Arc 22.69.1;
|
|
120
|
+
older versions leave them unguarded): suppression returns 202 until an allowed
|
|
112
121
|
emission (or the wait deadline), denial returns unauthorized with no data.
|
|
113
122
|
Keep safe data selection and initial
|
|
114
123
|
authorization on every query regardless of transport.
|
|
@@ -61,6 +61,17 @@ Requires Arc 22.106.0 for read protection. Older Arc has only the per-command
|
|
|
61
61
|
`[Unprotected("why")]` (`Cratis.Arc.Chronicle.ReadModels`; on a method it covers
|
|
62
62
|
the body, not injected parameters), or repair with a reactor.
|
|
63
63
|
|
|
64
|
+
Requires Arc 22.113.0 with Chronicle 19.76.0 for `ReadChanged` validation state
|
|
65
|
+
to include the conflicting event type/id and sequence number when the kernel
|
|
66
|
+
reports them. Older kernels can leave those details absent. Outside-decision
|
|
67
|
+
read-model injection (for example a custom command filter) is refused under
|
|
68
|
+
`On`; explicitly unprotected models stay advisory. Check `dotnet list package`
|
|
69
|
+
before asserting conflict detail fields.
|
|
70
|
+
|
|
71
|
+
Requires Arc 22.116.2 for aggregate application, rehydration and append to resolve
|
|
72
|
+
events from registrations, including explicitly registered types without an
|
|
73
|
+
`[EventType]` attribute. Unregistered events still fail; do not omit registration.
|
|
74
|
+
|
|
64
75
|
Under `On`, `IReadModels`, `IMongoCollection`, detached reads and immediate appends
|
|
65
76
|
through an injected `IEventLog` are refused; take the model as a parameter and
|
|
66
77
|
return the events. Admitted models: event-source-keyed model-bound or declarative
|
|
@@ -23,6 +23,9 @@ Operation execution,
|
|
|
23
23
|
nesting restrictions, and backend recovery members below are described in
|
|
24
24
|
`cratis-arc-command-operation`, which lists sources and limits.
|
|
25
25
|
|
|
26
|
+
Read references/occurrence-commands.md when an occurrence handler must execute a
|
|
27
|
+
command under a stable effect name with an atomic receipt and retry classification.
|
|
28
|
+
|
|
26
29
|
## When this is the wrong tool
|
|
27
30
|
|
|
28
31
|
`ICommandPipeline` is for **command-shaped** work that needs authorization and
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
# Execute a command for an occurrence
|
|
5
|
+
|
|
6
|
+
Requires Arc 22.117.0 and Chronicle client 19.77.2, plus a kernel advertising
|
|
7
|
+
`effect-receipts`. Check with `dotnet list package`; older Arc has no occurrence
|
|
8
|
+
executor. Do not substitute an ordinary pipeline call and claim durable deduplication.
|
|
9
|
+
|
|
10
|
+
## Choose the executor
|
|
11
|
+
|
|
12
|
+
Inject `IOccurrenceCommandExecutor` from `Cratis.Arc.Chronicle.Occurrences` into
|
|
13
|
+
an occurrence handler. Its overloads are:
|
|
14
|
+
|
|
15
|
+
```csharp
|
|
16
|
+
Task<OccurrenceCommandResult> Execute(
|
|
17
|
+
object command, OccurrenceContext occurrence, EffectName effectName,
|
|
18
|
+
CancellationToken cancellationToken = default);
|
|
19
|
+
|
|
20
|
+
Task<OccurrenceCommandResult> Execute(
|
|
21
|
+
object command, OccurrenceContext occurrence, EffectName effectName,
|
|
22
|
+
IEnumerable<string> systemRoles, CancellationToken cancellationToken = default);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`OccurrenceContext` and `EffectName` are Chronicle occurrence types. Read
|
|
26
|
+
**cratis-chronicle-client-dotnet** for recording triggers and handling lifecycle
|
|
27
|
+
occurrences. Do not call the executor from inside another command.
|
|
28
|
+
|
|
29
|
+
The executor supplies a system principal; no caller roles are inherited. Supply
|
|
30
|
+
only the explicit roles the automation needs. The occurrence namespace must
|
|
31
|
+
match the resolved command tenant; a mismatch throws. Authorization still runs.
|
|
32
|
+
|
|
33
|
+
Give each logical effect a stable, unique name within the occurrence. Retrying
|
|
34
|
+
uses the same name. Reusing it for a different command returns the earlier
|
|
35
|
+
receipt, not the new command's result. A deliberately new attempt at a terminal
|
|
36
|
+
business refusal needs a new effect name.
|
|
37
|
+
|
|
38
|
+
## Outcomes and retry
|
|
39
|
+
|
|
40
|
+
The executor checks for an existing receipt before running the pipeline. Its
|
|
41
|
+
receipt is committed atomically with the command's transactional events.
|
|
42
|
+
|
|
43
|
+
- `Recorded` and `AlreadyRecorded` are settled results. Their outcomes are
|
|
44
|
+
`Appended`, `NoEvents` or `Refused`.
|
|
45
|
+
- Validation, business-rule and append-constraint refusals are terminal and
|
|
46
|
+
recorded. Redelivery does not re-run the handler to reconsider them.
|
|
47
|
+
- Authorization failures, concurrency conflicts and exceptions are `Failed`
|
|
48
|
+
without a receipt, so a later delivery can retry. Reread and re-decide rather
|
|
49
|
+
than replaying stale events.
|
|
50
|
+
- The stored response is a minimal result, not a raw command response. It does
|
|
51
|
+
not preserve command values or validation messages. Never depend on a receipt
|
|
52
|
+
returning secret data or the original response object.
|
|
53
|
+
|
|
54
|
+
A receipt settles Chronicle effects, not arbitrary I/O. Two concurrent attempts
|
|
55
|
+
can both reach the handler before one wins the atomic commit. Use
|
|
56
|
+
`occurrence.EffectKey(effectName).Value` as the external service's idempotency
|
|
57
|
+
key where that service supports it; otherwise an external effect can repeat.
|
|
58
|
+
|
|
59
|
+
## Keep all appends inside the boundary
|
|
60
|
+
|
|
61
|
+
Return events from the command, or use transactional appends. Do not complete a
|
|
62
|
+
stream or commit an aggregate early inside the executor's command.
|
|
63
|
+
|
|
64
|
+
An immediate append can commit **before** the executor detects it and reports
|
|
65
|
+
`OccurrenceCommandAppendedImmediately`. No receipt is recorded for that failed
|
|
66
|
+
attempt, so retry can duplicate the escaped append. The executor does not
|
|
67
|
+
implicitly provide Chronicle's `RefuseImmediateAppends()` guard. Read
|
|
68
|
+
**cratis-chronicle-client-dotnet**'s immediate-append refusal reference when
|
|
69
|
+
building a custom boundary that needs prevention before transport.
|
|
70
|
+
|
|
71
|
+
## Verify
|
|
72
|
+
|
|
73
|
+
Use an in-process occurrence scenario with the command's real validation and
|
|
74
|
+
append behavior. Deliver the same occurrence twice under the same effect name:
|
|
75
|
+
assert one committed effect and a recorded result on the second delivery. Cover
|
|
76
|
+
a terminal refusal, a retryable concurrency conflict, insufficient capability,
|
|
77
|
+
and commit-then-lost-reply recovery. Run the owning specification project with
|
|
78
|
+
`dotnet test`; passing means those assertions ran, not just that compilation succeeded.
|
|
@@ -130,17 +130,11 @@ public class TransferFundsValidator : CommandValidator<TransferFunds>
|
|
|
130
130
|
instance validators are refused. Under `[ProtectedDecision]` without `On`, a
|
|
131
131
|
discoverable validator needs exactly one public parameterless constructor
|
|
132
132
|
(otherwise HTTP 500).
|
|
133
|
-
- **The caller can lower the severity threshold.**
|
|
134
|
-
`
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
`[BlockOnValidationSeverity(ValidationResultSeverity.Error)]`
|
|
139
|
-
(`Cratis.Arc.Commands.ModelBound`). Requires Arc 22.108.0 for the `On` floor: a
|
|
140
|
-
command declared `On` blocks `Error` and `Unknown` whatever the caller sends; the
|
|
141
|
-
caller can relax only `Warning` and `Information`. Lowering
|
|
142
|
-
`ReadProtection:MaximumMode` at runtime does not remove the floor, and an explicit
|
|
143
|
-
attribute wins. Severity is not authorization.
|
|
133
|
+
- **The caller can lower the severity threshold.** Under `Off`/`Measure`, pin
|
|
134
|
+
`[BlockOnValidationSeverity(ValidationResultSeverity.Error)]` when Error rules
|
|
135
|
+
must block. Requires Arc 22.108.0 for commands declared `On` to keep an Error
|
|
136
|
+
floor. Read references/validation-severity.md when choosing that threshold;
|
|
137
|
+
severity is not authorization.
|
|
144
138
|
- **Omit the validator entirely when there are no rules.** An empty subclass is
|
|
145
139
|
noise the reader has to check.
|
|
146
140
|
- Single-property intrinsic rules belong on `ConceptValidator<T>` instead, so
|
|
@@ -258,32 +252,12 @@ public Result<ItemAddedToCart, ValidationResult> Handle(CartSummary? cart) =>
|
|
|
258
252
|
- Keep the fetch for a *computed* key in `Provide()` with `IDecisionReads.Get`;
|
|
259
253
|
under `On`, `IReadModels` there is refused.
|
|
260
254
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
projection or reducer, an EF Core `DbSet<T>`, or a MongoDB collection backs it.
|
|
264
|
-
And there is **no slice or feature restriction**: registration is global by
|
|
265
|
-
type, so "the read model must be in the same slice" is a convention with nothing
|
|
266
|
-
enforcing it.
|
|
255
|
+
Read references/read-model-resolution.md when choosing lookup keys, handling a
|
|
256
|
+
missing injected model, or distinguishing registration from slice conventions.
|
|
267
257
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
command with more than one candidate identity, or an id generated inside
|
|
272
|
-
`Handle()` — direct injection hands you the wrong instance or none. Read by
|
|
273
|
-
explicit key in `Provide()` instead:
|
|
274
|
-
|
|
275
|
-
```csharp
|
|
276
|
-
var other = await readModels.GetInstanceById<Other>((EventSourceId)otherId);
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
A missing instance resolves to `null`, never to a default object. A nullable
|
|
280
|
-
parameter receives that `null`; a non-nullable one is **rejected as a validation
|
|
281
|
-
failure (400)**, not crashed. `ARC0006` warns on a non-nullable command-scoped
|
|
282
|
-
read-model parameter for exactly this reason. Handle a nullable parameter by rejecting (a validator,
|
|
283
|
-
`Provide()`, or `Result<TEvent, ValidationResult>`), never by returning a
|
|
284
|
-
nullable event from `Handle()`. `cratis-arc-command` carries the
|
|
285
|
-
full resolution and nullability reference, including the passive-projection case
|
|
286
|
-
where absence is *not* `null`.
|
|
258
|
+
Read references/input-binding.md when accepting date-time input or returning
|
|
259
|
+
member-bound validation results: offset requirements, malformed-body
|
|
260
|
+
authorization and camelCase member paths are separate contracts.
|
|
287
261
|
|
|
288
262
|
## Step 6 — Specify the rejection
|
|
289
263
|
|