Skip to content

Release digest: July 13 - 20, 2026 (Week 29)

  • Arc (Release v20.60.0): New ICommandExecutionScope extension point — implementations are discovered automatically and participate in every command’s execution, beginning before the command runs and completing exactly once, in isolation, with the final CommandResult.
  • Arc (Release v20.60.0): Documentation for transactional commands and command execution scopes.
  • Screenplay (Release v1.1.0): A new `specification` construct inside a `slice` — Given/When/Then test scenarios, following the same sub-language pattern as Projection and Capture (`ISpecificationSyntaxVisitor`, `CompileSpecification`/`CompileSpecification` on `IScreenplayCompiler`)
  • Screenplay (Release v1.1.0): `split` and `nested` operations in the Capture (CDL) language, plus richer `when` trigger forms (`and`/`or` combinators, value transitions, template-literal expressions) and a `map` block on `children`
  • Screenplay (Release v1.1.0): `ICaptureSyntaxVisitor` and `CompileCapture`/`CompileCapture` on `IScreenplayCompiler` — Capture was previously parseable but had no visitor/compiler entry point, unlike Projection
  • Screenplay (Release v1.0.0): Cratis.Screenplay NuGet package - the compiler for the Screenplay language: parser for the full grammar, plain data syntax tree, one visitor contract per language level (IApplicationSyntaxVisitor<T> through IProjectionSyntaxVisitor<T> and IConstraintSyntaxVisitor<T>), diagnostics with source positions, **/*.play file discovery and a compiler style diagnostic formatter
  • Screenplay (Release v1.0.0): Cratis.Screenplay.Tool NuGet package - the screenplay dotnet tool that finds and compiles every .play file beneath a directory and reports problems with source excerpts and carets
  • Screenplay (Release v1.0.0): Standalone projection compilation entry point for compiling the projection sub-language outside an enclosing slice
  • Screenplay (Release v1.0.0): Projection language reference as a sub-section of the Screenplay language documentation, plus a Compiler and CLI page covering installation and use
  • Arc (Release v20.59.0): IAuthorizationQueryFilter for marking a query filter to be evaluated before other query filters.
  • Arc (Release v20.58.0): ArcOptions.ExposeExceptionDetails to control whether command and query error responses include exception messages and stack traces. It defaults to enabled only in the Development environment.
  • Arc (Release v20.58.0): IValidationFailure — an exception implementing it is surfaced as a command validation failure (HTTP 400) instead of a server error, so code that runs inside the command pipeline can reject a command as invalid by throwing.
  • Arc (Release v20.58.0): New analyzer ARC0013 — warns when a validator rule dereferences a member of a possibly-null concept (for example RuleFor(c => c.Order.Value)), which throws at validation time instead of producing a validation error. Validate the concept first, or guard the rule with .When(...).
  • Arc (Release v20.57.0): New analyzer ARC0013 — warns when a validator rule dereferences a member of a possibly-null concept (for example RuleFor(c => c.Order.Value)). Because model-bound input can be deserialized with a null concept, the rule throws at validation time instead of producing a validation error; the analyzer flags it in the editor and points to the fixes (validate the concept when required, or guard the rule with .When(...)).
  • Arc (Release v20.56.0): Queries can now be invoked with the HTTP QUERY method (RFC 10008), sending arguments, paging and sorting as a JSON request body instead of the URL query string — useful for large or sensitive query arguments that don’t belong in a URL.
  • Arc (Release v20.56.0): [QueryHttpMethod] attribute on a read model or a static query method declares the client transport in C#; it flows through proxy generation so the generated proxy uses that method by default (a method-level attribute overrides a read-model-level one).
  • Arc (Release v20.56.0): On the client, opt in per query with query.setHttpMethod(QueryHttpMethod.Query), globally with Globals.queryHttpMethod, or with QueryHttpMethod.Auto which prefers QUERY and falls back to GET (remembering the outcome per backend).
  • Arc (Release v20.56.0): Globals.queryHttpMethodResolver for choosing the transport per request, with a built-in lengthBasedQueryHttpMethod policy that keeps short queries on GET and prefers QUERY only when the URL would exceed a threshold.
  • Arc (Release v20.56.0): ArcOptions.GeneratedApis.EnableQueryHttpMethod (default true) to disable the QUERY endpoints and restrict queries to GET only.
  • Arc (Release v20.56.0): ISystemExecution for executing [Roles]/[Authorize] commands from server-side or background code (reactors, hosted services, sagas, command-to-command) as a trusted system actor carrying named roles, via an explicit using systemExecution.AsSystem(...) scope.
  • Arc (Release v20.56.0): [ExecuteCommandsAsSystem] attribute to run a reactor’s returned commands under declared roles automatically — opt-in; reactors without it are unchanged.
  • Chronicle (Release v16.1.1): Optional dedicated health endpoint port (Cratis:Chronicle:Health:Port) that publishes the health endpoint on its own HTTP/1.1 port, with TLS optionally disabled (Health:Tls) for orchestrator and load-balancer probes that cannot validate a self-signed certificate. When unset, the health endpoint stays on the main port.
  • Chronicle (Release v16.1.1): skipTlsValidation connection-string option (and SkipCertificateValidation on the client TLS options) to connect over TLS without validating the server certificate — for a trusted server on a trusted network. The built-in development connection string enables it so development works against the server’s self-signed certificate.
  • AuthProxy (Release v2.6.0): Invite.EmailClaim configuration (default email) naming the invite-token claim that carries the email an invitation was issued for. Set it to an empty string to disable email-binding enforcement.
  • AuthProxy (Release v2.6.0): invitation-email-mismatch.html well-known page, served (HTTP 403) when the signed-in account’s verified email does not match the invited email.
  • Chronicle (Release v16.0.5): Tls:Enabled=false runs Chronicle in cleartext: h2c gRPC on the main port and the Workbench, REST API, OAuth and health surface on ManagementPort (8080 by default). Use it when TLS is terminated upstream by a load balancer/ingress, or for local development with no certificate.
  • Chronicle (Release v16.0.5): Optional HealthPort exposes the /health endpoint on a dedicated plaintext port for orchestrator and load-balancer probes, while gRPC, the API and OAuth stay on TLS on the main port.
  • Arc (Release v20.60.0): Every model-bound command now runs in a transactional scope. The events a command returns from Handle() — and appends made through the explicit eventLog.Transactional style — commit atomically when the command succeeds and roll back if it fails for any reason, in both the all-in-one AddCratis setup and AddCratisArc().WithChronicle(). Constraint and concurrency violations from the commit surface on the CommandResult, attributed to the offending member so a form can attach the message to its field.
  • Arc (Release v20.60.0): Appends through an injected IEventLog behave identically to IEventStore.EventLog and every other event sequence — immediate and final, returning the real AppendResult — and are never silently swallowed: a failed immediate append during a command fails the command. Previously such a failure could be discarded, leaving the command reporting a false success.
  • Screenplay (Release v1.1.0): Capture’s `map`/`split` source values are now a full expression (property path or template literal) instead of a plain string, matching how Projection and capture `append` mappings already work
  • Chronicle (Release v16.3.1): The projection declaration compiler now uses the Cratis Screenplay compiler for parsing; ILanguageService.Compile, code generation and PDL round tripping behave as before
  • Chronicle (Release v16.3.1): $causedBy identity property references are camelCase (subject, name, userName), consistent with the well known expressions
  • Chronicle (Release v16.3.1): Namespace qualified projection names round trip through compile and generate
  • Chronicle (Release v16.3.1): The projection language reference moved to the Screenplay documentation; Chronicle’s pages and the Workbench help panel link there
  • Screenplay (Release v1.0.0): The publish workflow now also publishes the NuGet packages using trusted publishing, and pull requests build and test the .NET solution
  • Arc (Release v20.59.0): Query filters now short-circuit on the first blocking verdict, and authorization filters run before other query filters, so an unauthorized-and-invalid query consistently returns 403 Forbidden.
  • Arc (Release v20.59.0): An observable query that has not yet produced its first result is now reported as not ready (202 Accepted, no exception, new isReady flag on the query result) rather than an error-shaped result, so callers no longer read a pending query as a crash.
  • Arc (Release v20.59.0): Behavior change: a command carrying a non-nullable concept property that arrives null is now rejected with 400 Bad Request. Previously such a command could succeed if its handler never dereferenced the value. Declare the concept nullable (Concept?) to keep it optional; the event source key is exempt.
  • Chronicle (Release v16.2.0): [PII] values are now protected with hybrid AES-256-GCM envelope encryption (a fresh per-value data key wrapped by the per-subject RSA key) instead of raw RSA. Values written by earlier versions continue to decrypt transparently.
  • Arc (Release v20.56.0): Every generated query endpoint now also accepts the QUERY method alongside GET; the GET behavior is unchanged. The OpenAPI/Swagger document notes the QUERY option on each query operation.
  • Arc (Release v20.56.0): [Roles]/[Authorize] commands can now be authorized from non-HTTP callers through the new server-side execution scope. HTTP requests are unaffected — the request principal stays authoritative and request data can never enter the scope.
  • Arc (Release v20.55.0): MVVM now defers MobX reactions to a microtask by default, so an observable that resolves mid-render no longer triggers React’s “Cannot update a component while rendering a different component” error. The default is overridable through the mobx reactionScheduler option.
  • Chronicle (Release v16.1.1): The .NET client now validates the server certificate by default. A self-signed or otherwise untrusted certificate is rejected unless validation is skipped or a matching client certificate is configured.
  • Arc (Release v20.54.4): Update Cratis.Chronicle to 16.1.0
  • Arc (Release v20.54.4): Update Cratis.Fundamentals to 7.16.6
  • Chronicle (Release v16.1.0): Fluent projection accessors that are not a simple member access (string interpolation, arithmetic, method calls, constants, or expressions that ignore the parameter such as _ => DateTimeOffset.UtcNow) now throw a descriptive InvalidPropertyExpression at definition time instead of silently mapping to nothing. This applies across the whole fluent projection surface — value/key accessors and target-property selectors. A projection that relied on such an accessor was already projecting nothing; it now fails loud at registration.
  • AuthProxy (Release v2.6.3): The publish pipeline now fails loudly if the frontend build output is empty, instead of silently shipping an image without the frontend.
  • AuthProxy (Release v2.6.1): Invite.EmailClaim now defaults to empty, so gateway email-binding enforcement is off by default. Set it (for example to email) to require the authenticated account’s verified email to match the invited email at the Phase-2 exchange. The verified email is forwarded to the exchange endpoint regardless of this setting, so the backend can still enforce the binding itself.
  • AuthProxy (Release v2.6.0): The Phase-2 invite exchange request now forwards the authenticating account’s provider-verified email and its verification status, so the backend can apply its own defense-in-depth check of invited-email vs. authenticated-email at accept time.
  • Chronicle (Release v16.0.5): The startup error for a missing production certificate now states that TLS can be disabled with Tls:Enabled=false, rather than only that a certificate is required.
  • Arc (Release v20.60.0): An unknown sortby field on a MongoDB-backed query now degrades to unsorted results instead of failing with an HTTP 500, matching the Entity Framework Core provider’s behavior.
  • Arc (Release v20.60.0): A failure while completing a command’s transaction — for example the event store being unreachable at commit — now surfaces as an exception outcome on the CommandResult instead of escaping as a raw exception to programmatic callers.
  • Screenplay (Release v1.0.3): $causedBy.userName is now the accepted form for the username identity property, consistent with the camelCase well known expressions (subject, name, userName)
  • Arc (Release v20.59.1): A command declaring more than one event-source-key concept now returns HTTP 400 (not a 500) when a secondary key arrives null, with the failure pointing at the offending field.
  • Arc (Release v20.59.1): Validation failures raised by a nested ConceptValidator<T> rule (for example RuleFor(x => x.Value)) now surface on the owning command form field. Previously the failure was reported against the concept’s inner Value member and was never displayed during write-time (blur/change) validation.
  • Arc (Release v20.59.0): A command that receives a wrong-typed value for a Guid-, number-, or enum-backed concept property now returns 400 Bad Request instead of 500.
  • Arc (Release v20.59.0): Omitting a required (non-nullable) concept from a command now returns 400 Bad Request identifying the field, instead of 500.
  • Arc (Release v20.59.0): A command that depends on a must-exist read model which does not exist for its identifier now returns 400 Bad Request instead of a 500 that revealed the read model type.
  • Arc (Release v20.59.0): A query filter that throws no longer crashes the whole query into a 500, and a denied query can no longer be turned into a 500 by a later filter.
  • Arc (Release v20.59.0): Out-of-range or hostile paging on an observable query no longer overflows into a server error.
  • Chronicle (Release v16.2.1): An unset optional (nullable) scalar on a read model now materializes as null/absent instead of a type-default sentinel — e.g. a DateTimeOffset? no longer reads back as 0001-01-01 when its source event never fired, and an int? no longer reads back as 0.
  • Arc (Release v20.58.1): A command that fails role authorization now returns 403 Forbidden even when the command is also invalid, instead of a 400 validation error.
  • Arc (Release v20.58.0): Malformed or wrong-typed command request bodies now return HTTP 400 instead of HTTP 500.
  • Arc (Release v20.58.0): A command validator that throws while validating hostile or partial input now returns a validation error (HTTP 400) instead of HTTP 500.
  • Arc (Release v20.58.0): A command that targets an aggregate but provides no usable event source id now returns a validation error (HTTP 400) instead of crashing with HTTP 500, with a client-safe message that does not leak the read model type.
  • Arc (Release v20.58.0): A throwing command filter no longer aborts the filter chain or masks an earlier authorization result — a forbidden request now returns HTTP 403 instead of HTTP 500.
  • Arc (Release v20.58.0): Observable queries no longer fail with HTTP 500 (or leak the read model type name) on an unknown sort field, and now clamp a non-positive page size to a valid page — for both MongoDB and Entity Framework Core read models.
  • Chronicle (Release v16.2.0): [PII] values longer than ~245 bytes (long free-text fields, images) no longer throw a CryptographicException at append time — arbitrary-length PII now encrypts and decrypts.
  • Chronicle (Release v16.2.0): A root-level [Join] whose source stream key type differs from the read model’s key (for example a string organization number joined onto a Guid-keyed root) no longer permanently freezes the observer partition with an “Unrecognized Guid format” error, and no longer materializes a phantom root document keyed by the join value; the join backfills when the real entity row appears.
  • Chronicle (Release v16.2.0): IReadModels.GetInstanceById now returns null for an unseeded reducer-backed read model instead of throwing, matching projection-backed read models — so a reactor’s nullable pre-check no longer throws and freezes the partition.
  • Chronicle (Release v16.2.0): EventScenario (in Cratis.Chronicle.Testing) now short-circuits a multi-event Events(...) append on the first rejected event, returning that failed AppendResult instead of continuing and masking the rejection with a later event’s success.
  • Arc (Release v20.55.0): Observable (SSE/WebSocket) queries with a ConceptAs<T> parameter no longer fail — query arguments are now coerced to their declared parameter types on the streaming path, matching one-shot queries.
  • Arc (Release v20.55.0): A command whose Handle() returns a boxed IEnumerable<object> of EventForEventSourceId wrappers, or a mixed collection of plain events and wrappers, now appends the events instead of silently reporting success with nothing appended.
  • Arc (Release v20.54.6): Added a missing index.md landing page for the Reactors docs folder — it had a toc.yml and one topic page but no landing, so its bare URL 404’d and broke the Documentation site’s internal link check.
  • Arc (Release v20.54.5): Restore/build failure from a Cratis.Metrics.Roslyn package downgrade after updating to Cratis.Chronicle 16.1.0
  • Chronicle (Release v16.1.0): Observable read-model queries (ObserveById / ObserveSingle / live queries) now decrypt [PII] values when the projected document carries no explicit __subject, resolving the compliance subject the same way one-shot queries do.
  • Chronicle (Release v16.1.0): ReadModelScenario<T> now backfills a [Join] value regardless of seed order, for both root-level and child-collection joins and any key type.
  • Fundamentals (Release v7.16.6): The Types constructor no longer crashes application startup when a provider’s DefinedTypes or SelfBindings sequence throws TypeLoadException during enumeration (for example a generated provider referencing a type that a resolved assembly version no longer contains). The elements collected before the failure are kept and discovery continues.
  • Fundamentals (Release v7.16.5): Type discovery (ContractToImplementorsMap) no longer aborts the whole scan — and the process — when inspecting a type whose base or interface types throw TypeLoadException (for example when a referenced assembly resolves to a version that no longer contains a referenced type). The unloadable type is skipped and the remaining types are still discovered.
  • Fundamentals (Release v7.16.5): ProjectReferencedAssemblies now tolerates assemblies whose DefinedTypes fail to enumerate, matching the existing behavior of PackageReferencedAssemblies.
  • AuthProxy (Release v2.6.3): The React frontend is now built into the AuthProxy web root and bundled into the published image, instead of being written to a stale Ingress path and dropped.
  • Chronicle (Release v16.0.6): A model-bound [Join] whose key is an AutoMapped foreign key now resolves its joined value when the read-model row is created after the join-source event already exists. Previously these columns were left empty whenever the join source (e.g. an activated engagement) preceded the row-creating fact (e.g. an approved timesheet) — the common production ordering.
  • Chronicle (Release v16.0.5): Tls:Enabled=false no longer crashes the server on startup — it now produces a working plaintext server, matching the property’s documentation.
  • Arc (Release v20.54.3): CommandForm now calls the validateClientSide for validating things when the automatic serverside validation is turned off.
  • Chronicle (Release v16.0.4): Active (non-passive) read models with [PII] persisted to a SQL sink (PostgreSQL, SQL Server, SQLite) stalled and never materialized out of process — the query returned null indefinitely with no error or observer quarantine, and PII could fail to decrypt on read. MongoDB-backed read models were unaffected. (#3463)
  • Chronicle (Release v16.0.3): Fixed lock contention in the client’s scoped IEventStore resolution: a single process-wide lock serialized every resolution across all namespaces and requests, and held that lock across a blocking call, which could stall unrelated requests and surface as Cannot access a disposed object: GrpcChannel under concurrent load.
  • Chronicle (Release v16.0.3): Fixed an activation access violation in the projection replay job step (HandleEventsForObserver): it read the ambient Grain.GrainFactory property from code that runs off the grain’s activation thread, throwing “Activation access violation” and leaving the observer stuck without processing new events even though they were present in the log.
  • Chronicle (Release v16.0.3): [x] dotnet build (Debug + Release) for the affected client and kernel projects — zero warnings, zero errors
  • Chronicle (Release v16.0.3): [x] dotnet test Source/Kernel/Core.Specs — 1612 passed, 1 pre-existing skip, 0 failures
  • Chronicle (Release v16.0.3): [x] Verified against a downstream consumer app under Aspire-orchestrated concurrent startup — repeated onboarding flow runs now produce identical, deterministic pass/fail results instead of the prior non-deterministic 6-10/30 range with silent disposed-channel failures
  • Chronicle (Release v16.3.1): The internal PDL tokenizer, parser and AST - replaced by the Cratis.Screenplay syntax tree and visitor contracts
  • Chronicle (Release v16.1.1): The client disableTls connection-string option and the WithTlsDisabled/WithTlsEnabled builder methods. The server always serves TLS, so a plaintext client connection no longer connects to anything.
  • Arc (Release v20.58.1): Validation detail is no longer returned to a caller who is not authorized for a command endpoint — authorization is decided before validation runs.
  • Arc (Release v20.58.0): Exception messages and stack traces are no longer returned to clients in command and query responses outside the Development environment. The full detail is logged server-side and the correlation id is retained, so failures can still be traced without leaking internal information.
  • Chronicle (Release v16.1.1): The client no longer implicitly accepts self-signed or name-mismatched server certificates in production; validation must be skipped explicitly via skipTlsValidation.
  • AuthProxy (Release v2.6.0): Re-validate the invite token (RSA signature, issuer, audience, and lifetime) at the Phase-2 exchange forward and refuse to forward when it fails. HTTP-only cookies only block browser JavaScript, so an authenticated caller could otherwise hand a self-crafted token to the exchange endpoint.
  • AuthProxy (Release v2.6.0): Bind an invitation to its recipient: when the invite token targets a specific email, require the authenticating account’s verified email to match (respecting email_verified) and reject on mismatch, so an invitation can no longer be redeemed with a different account.
  • No updates