Release digest: July 13 - 20, 2026 (Week 29)
- Arc (Release v20.60.0): New
ICommandExecutionScopeextension 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 finalCommandResult. - 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.ScreenplayNuGet package - the compiler for the Screenplay language: parser for the full grammar, plain data syntax tree, one visitor contract per language level (IApplicationSyntaxVisitor<T>throughIProjectionSyntaxVisitor<T>andIConstraintSyntaxVisitor<T>), diagnostics with source positions,**/*.playfile discovery and a compiler style diagnostic formatter - Screenplay (Release v1.0.0):
Cratis.Screenplay.ToolNuGet package - thescreenplaydotnet tool that finds and compiles every.playfile 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):
IAuthorizationQueryFilterfor marking a query filter to be evaluated before other query filters. - Arc (Release v20.58.0):
ArcOptions.ExposeExceptionDetailsto 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
QUERYmethod (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 withGlobals.queryHttpMethod, or withQueryHttpMethod.Autowhich prefersQUERYand falls back toGET(remembering the outcome per backend). - Arc (Release v20.56.0):
Globals.queryHttpMethodResolverfor choosing the transport per request, with a built-inlengthBasedQueryHttpMethodpolicy that keeps short queries onGETand prefersQUERYonly when the URL would exceed a threshold. - Arc (Release v20.56.0):
ArcOptions.GeneratedApis.EnableQueryHttpMethod(defaulttrue) to disable theQUERYendpoints and restrict queries toGETonly. - Arc (Release v20.56.0):
ISystemExecutionfor 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 explicitusing 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):
skipTlsValidationconnection-string option (andSkipCertificateValidationon 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.EmailClaimconfiguration (defaultemail) 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.htmlwell-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=falseruns Chronicle in cleartext: h2c gRPC on the main port and the Workbench, REST API, OAuth and health surface onManagementPort(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
HealthPortexposes the/healthendpoint on a dedicated plaintext port for orchestrator and load-balancer probes, while gRPC, the API and OAuth stay on TLS on the main port.
Changed
Section titled “Changed”- 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 expliciteventLog.Transactionalstyle — commit atomically when the command succeeds and roll back if it fails for any reason, in both the all-in-oneAddCratissetup andAddCratisArc().WithChronicle(). Constraint and concurrency violations from the commit surface on theCommandResult, attributed to the offending member so a form can attach the message to its field. - Arc (Release v20.60.0): Appends through an injected
IEventLogbehave identically toIEventStore.EventLogand every other event sequence — immediate and final, returning the realAppendResult— 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):
$causedByidentity 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, newisReadyflag 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
nullis now rejected with400 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
QUERYmethod alongsideGET; theGETbehavior is unchanged. The OpenAPI/Swagger document notes theQUERYoption 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
mobxreactionScheduleroption. - 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 descriptiveInvalidPropertyExpressionat 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.EmailClaimnow defaults to empty, so gateway email-binding enforcement is off by default. Set it (for example toemail) 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
sortbyfield 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
CommandResultinstead of escaping as a raw exception to programmatic callers. - Screenplay (Release v1.0.3):
$causedBy.userNameis 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 exampleRuleFor(x => x.Value)) now surface on the owning command form field. Previously the failure was reported against the concept’s innerValuemember 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 returns400 Bad Requestinstead of500. - Arc (Release v20.59.0): Omitting a required (non-nullable) concept from a command now returns
400 Bad Requestidentifying the field, instead of500. - 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 Requestinstead of a500that 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 a500by 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. aDateTimeOffset?no longer reads back as0001-01-01when its source event never fired, and anint?no longer reads back as0. - Arc (Release v20.58.1): A command that fails role authorization now returns
403 Forbiddeneven when the command is also invalid, instead of a400validation 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 aCryptographicExceptionat 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 aGuid-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.GetInstanceByIdnow returnsnullfor 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(inCratis.Chronicle.Testing) now short-circuits a multi-eventEvents(...)append on the first rejected event, returning that failedAppendResultinstead 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 boxedIEnumerable<object>ofEventForEventSourceIdwrappers, 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.mdlanding page for the Reactors docs folder — it had atoc.ymland 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
Typesconstructor no longer crashes application startup when a provider’sDefinedTypesorSelfBindingssequence throwsTypeLoadExceptionduring 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 throwTypeLoadException(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):
ProjectReferencedAssembliesnow tolerates assemblies whoseDefinedTypesfail to enumerate, matching the existing behavior ofPackageReferencedAssemblies. - 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
Ingresspath 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=falseno longer crashes the server on startup — it now produces a working plaintext server, matching the property’s documentation. - Arc (Release v20.54.3):
CommandFormnow calls thevalidateClientSidefor 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 returnednullindefinitely 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
IEventStoreresolution: 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 asCannot access a disposed object: GrpcChannelunder concurrent load. - Chronicle (Release v16.0.3): Fixed an activation access violation in the projection replay job step (
HandleEventsForObserver): it read the ambientGrain.GrainFactoryproperty 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
Removed
Section titled “Removed”- Chronicle (Release v16.3.1): The internal PDL tokenizer, parser and AST - replaced by the
Cratis.Screenplaysyntax tree and visitor contracts - Chronicle (Release v16.1.1): The client
disableTlsconnection-string option and theWithTlsDisabled/WithTlsEnabledbuilder methods. The server always serves TLS, so a plaintext client connection no longer connects to anything.
Security
Section titled “Security”- 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.
Deprecated
Section titled “Deprecated”- No updates