Release digest: July 20 - 27, 2026 (Week 30)
- Chronicle (Release v16.7.0):
ReactorScenario<T>.Servicescollection (with logging registered by default) and anAction<IServiceCollection>constructor for registering a reactor’s dependencies. - Chronicle (Release v16.7.0):
Given.ForEventSourceId(id).ReadModel(...)to seed read-model handler-method parameters in a reactor scenario. - Chronicle (Release v16.7.0):
ReactorScenario<T>.ShouldHaveProduced<T>()/ShouldNotHaveProduced<T>()and aProducedlist to assert the events and commands a reactor returns as side effects, with no mocked event store. - Chronicle (Release v16.7.0):
ReadModelScenario<T>.Servicescollection for registering reducer and projection dependencies without supplying a fullIServiceProvider. - Chronicle (Release v16.7.0):
CannotActivateReactorForScenario, thrown with the name of the dependency that could not be resolved when a reactor cannot be constructed. - release-action (Release v1.1.2): Documentation for the required
contents: writepermission, for choosing between thepull_requestandpushtriggers (apushtrigger keeps releases working for fork pull requests, whose token is read-only), and for manualworkflow_dispatchruns. - Chronicle (Release v16.6.0): Configuration options for previously-hardcoded operational timeouts — connected-client intervals, the webhook HTTP resilience pipeline, the SQL live-query polling interval, the event-queue depletion wait, and the subscription-ready timeout — each defaulting to its current value (#1417)
- Chronicle (Release v16.6.0): The ability to rename an identity’s display name, from kernel storage through gRPC to the client SDK, keyed by subject so it never affects the PII encryption key (#1684)
- Chronicle (Release v16.6.0): React to read model changes with
IReadModelReactor: methods namedAdded,ModifiedorRemovedare invoked by convention with the changed read model (a single instance or a collection), an optionalEventContext, and services, and may return events as side effects (#3359) - Chronicle (Release v16.6.0):
[Materialized]opts a read model into materialized change tracking, deducing the change type by comparing successive materialized windows (#3359) - Chronicle (Release v16.6.0): OpenTelemetry tracing spans for client unit-of-work commit and rollback, tagged with the correlation id (#1396)
- release-action (Release v1.1.0):
major-labels,minor-labelsandpatch-labelsinputs to configure which label names drive each version bump. - release-action (Release v1.1.0):
tag-prefixinput to configure the prefix used for the release tag. - release-action (Release v1.1.0):
tagandprevious-versionoutputs. - release-action (Release v1.1.0): A decision table written to the job summary explaining what the run decided.
- AuthProxy (Release v2.10.0): Sign-in notification back-channel: on a genuine logged-out → signed-in transition, AuthProxy
POSTs{ subject, identityProvider, ipAddress, location, browser, operatingSystem, userAgent }to a configured application endpoint. - AuthProxy (Release v2.10.0): New configuration key
Cratis:AuthProxy:SignIn:NotifyUrl, alongside the existingInvite:ExchangeUrlandLink:ExchangeUrl. Leave it unset to disable notifications. - AuthProxy (Release v2.10.0): Approximate location derived from the client IP (honoring
X-Forwarded-For) and any geo headers a fronting CDN/proxy adds (CF-IPCountry,X-Geo-*,X-AppEngine-*) — no heavy geo-IP dependency; when no geo headers are present only the IP travels. - AuthProxy (Release v2.10.0): Lightweight built-in
User-Agentparsing into browser and operating system, with the raw header also forwarded. - AuthProxy (Release v2.10.0): Documentation:
Documentation/configuration/sign-in.md, wired into the configuration TOC. - AuthProxy (Release v2.10.0): The notification fires only from the provider callback (
OnTicketReceived), so it is scoped to real sign-ins — never on reused sessions or ordinary proxied requests. The credential-link flow short-circuits before this point, so a link is never reported as a sign-in. - AuthProxy (Release v2.10.0): Recording a sign-in is best-effort: any failure is logged and swallowed, and never breaks the sign-in itself.
- AuthProxy (Release v2.9.0):
/.cratis/logoutnow performs OIDC RP-initiated logout: for a session established through an OIDC provider it redirects the browser to the provider’send_session_endpointwithid_token_hintand apost_logout_redirect_uripointing at the new/.cratis/logout/callback, which then clears every AuthProxy cookie and redirects to the validated final target. - AuthProxy (Release v2.9.0):
Cratis:AuthProxy:...OIDC applications should registerhttps://<proxy-host>/.cratis/logout/callbackas a permitted post-logout redirect URI. - AuthProxy (Release v2.8.0):
GET /.cratis/link/{scheme}— a session-preserving credential-linking challenge. It authenticates the requested provider for an already signed-in user but, on the provider callback, captures the authenticatedsubjectand posts it to the application instead of signing the new identity in, so the user’s primary session is preserved. The one-time link token and a same-site-onlyreturnUrltravel through the flow. - AuthProxy (Release v2.8.0):
Cratis:AuthProxy:Link:ExchangeUrlconfiguration — the application back-channel endpoint AuthProxy posts{ subject, identityProvider }to (with the link token as the bearer credential), the link counterpart ofCratis:AuthProxy:Invite:ExchangeUrl. - AuthProxy (Release v2.7.0):
GET /.cratis/logout— signs the user out of the authentication cookie and clears every AuthProxy session cookie (.cratis-identity,.cratis-tenant,.cratis-tenants,.cratis-invite,.cratis-registration,.cratis-providers), then redirects to the absolute URL in theredirectquery parameter. The target is validated against an allow-list of origins (the proxy’s own public origin plus configured service/lobby frontends) so it cannot be used as an open redirect; a missing or disallowed target falls back to the application root. The endpoint is anonymous and runs before the authentication-challenge stages, so it works without a valid session. - Screenplay (Release v1.5.2): The invoicing sample now includes a CancelledInvoices slice whose projection has no explicit mappings at all — showcasing AutoMap’s default behavior copying every matching property from the event
- cli (Release v2.2.0):
cratis llmgroup — configure the language model used by Cratis tools:use <anthropic|openai|local>(interactive prompts, secret API-key input),show(masked key), andclear - cli (Release v2.2.0):
cratis prologue start— interactive wizard producing acratis-prologue.jsoncapture definition (SQL Server/PostgreSQL sources, API reverse-proxy capture, OpenTelemetry, JSON or Receiver API output) - cli (Release v2.2.0):
cratis prologue interpret [PATH]— interprets captured data into a Screenplay.playfile, streaming interpreter status and asking the interpreter’s questions one at a time with multiple choice plus a free-text “Other” option; output path optional, defaulting to the derived system name - Prologue (Release v1.1.0): Database schema captured as evidence: the Extractor reads the full schema (column types, sizes, nullability, primary keys, foreign keys, unique constraints) from SQL Server and PostgreSQL sources and emits it as a
database-schemaobservation - Prologue (Release v1.1.0): Schema evidence drives interpretation: property types from column types, required/max-length validation rules, and unique constraints on the extracted model
- Prologue (Release v1.1.0):
Cratis.Prologue.Interpretationpackage — a resumable interpreter session engine with checkpoint questions: the language model can ask the user multiple-choice questions (always with a free-text option) when uncertain, one at a time, and sessions park, persist, and resume statelessly at any time - Prologue (Release v1.1.0): Structured LLM refinement producing intentful names, a derived system name, and descriptions for every module, feature, slice, and command
- Prologue (Release v1.1.0):
Cratis.Prologue.Screenplaypackage — generates a Screenplay.playdocument from an extraction result using the Screenplay syntax tree and printer - Prologue (Release v1.1.0): Interpreter service mode: an Orleans-backed session per Prologue behind an HTTP API, with MongoDB-persisted state, a configurable grace period for unanswered questions, and clean self-exit so orchestrators can restart-to-resume
- Prologue (Release v1.1.0): Interpreter batch mode now also writes the generated
.playfile next to the extraction result - Prologue (Release v1.1.0): Extraction contract carries the system name, descriptions, validation rules, and constraints
- Screenplay (Release v1.5.0): Editor syntax highlighting for the
specification,given,thenanderrorkeywords. - Screenplay (Release v1.5.0): Editor syntax highlighting for the remaining projection keywords (
sequence,all,nested,automap,no,exclude,clear,via,literal) and capture keywords (split,nested,from,to,and,or,tag). - Screenplay (Release v1.5.0): Editor syntax highlighting for the
$eventSourceIdand$causedBycontext variables. - Screenplay (Release v1.4.0):
descriptionon commands — explain the logic and intent of a command directly in the language (#20) - Screenplay (Release v1.4.0): Multi-line descriptions on modules, features, slices, personas, and commands using the ``` fence convention, consistent with code blocks; the single-line
description "..."form stays supported (#20) - Screenplay (Release v1.4.0): Editor support for fenced description blocks (Monaco tokenizer states and VS Code TextMate scoping)
- Screenplay (Release v1.3.0):
description "..."on modules, features, and slices — a human explanation carried in the syntax tree and round-tripped by the printer - Screenplay (Release v1.3.0):
domaindeclaration for specifying which domain a file belongs to (#9) - Screenplay (Release v1.3.0):
personadeclarations with a description and associated policies (#18) - Screenplay (Release v1.3.0):
authenticationblock for declaring identity providers with free-form settings that can reference secrets (#17) - Screenplay (Release v1.3.0):
$secrets.<name>expressions plus encrypted.secretscompanion files (AES-256-GCM,enc:v1:format) with discovery, parsing, and a pluggable cipher (#16) - Screenplay (Release v1.3.0): Internationalization:
<base>.<locale>.stringscompanion files with discovery and parsing,$strings.<key>expressions, and$stringsreferences for validation messages and screen labels/titles (#7) - Screenplay (Release v1.3.0):
validateandvalidate csharpblocks on concepts, using the same rule grammar as commands with the concept value as the implied subject (#8) - Screenplay (Release v1.3.0):
seedblocks for declaring events to seed per event source (#10) - Screenplay (Release v1.3.0):
given readmodelandthen readmodelin specifications for read-model state preconditions and expectations (#11) - Screenplay (Release v1.3.0):
concurrencyblocks on commands expressing the concurrency scope dimensions (event source, source type, stream type, stream id, event types) (#13) - Screenplay (Release v1.3.0):
tagsupport on events,producesblocks, and captureappendblocks — static and context-resolved dynamic tags (#14) - Screenplay (Release v1.3.0): Editor support (Monaco tokenizer, hover docs, VS Code TextMate grammar) for all new keywords
- Chronicle (Release v16.5.0): Configuration options for operational timeouts that were previously hardcoded — connected-client revise/reservation-TTL/observe/keep-alive/stale-threshold intervals, the webhook HTTP resilience pipeline (retry delay, circuit-breaker sampling/break durations, request timeout, test timeout), the SQL live-query polling interval, the event-queue depletion wait, and the subscription-ready timeout — each defaulting to its current value (#1417)
- Chronicle.Elixir (Release v2.1.2): Added
Chronicle.Connections.Connection.reconnect/1for forcing a fresh channel when a caller has independent evidence the current one is unusable - Chronicle.TypeScript (Release v2.1.0):
chronicle://host1:port,host2:port,...connection strings — the client now connects to any one of several configured hosts instead of only a single server. - Chronicle.TypeScript (Release v2.1.0):
chronicle+srv://connection strings, resolving the target host(s) via a DNS SRV lookup, re-resolved on every connect/reconnect so membership changes are picked up automatically. The DNS server to query can be overridden with thesrvNameServeroption. - Chronicle.TypeScript (Release v2.1.0): A
loadBalancerconnection string option selecting how one address is picked among several candidates:least-connections(the default — probes each candidate and picks the one with the fewest active connections),round-robin, orrandom. - Chronicle.TypeScript (Release v2.1.0): A
skipTlsValidationconnection string option to skip TLS certificate validation independently of disabling TLS entirely. Defaults totrue, matching the client’s previous behavior — setskipTlsValidation=falseto require full certificate chain validation. - Chronicle.TypeScript (Release v2.1.0): Client connections now report the process id, process path, machine name, and client type to the Chronicle kernel, improving connection diagnostics.
- Chronicle.Kotlin (Release v2.1.0):
chronicle://host1:port,host2:port,...connection strings — the client now connects to any one of several configured hosts instead of only a single server. - Chronicle.Kotlin (Release v2.1.0):
chronicle+srv://connection strings, resolving the target host(s) via a DNS SRV lookup, re-resolved on every connect/reconnect so membership changes are picked up automatically. The DNS server to query can be overridden with thesrvNameServeroption. - Chronicle.Kotlin (Release v2.1.0): A
loadBalancerconnection string option selecting how one address is picked among several candidates:least-connections(the default — probes each candidate and picks the one with the fewest active connections),round-robin, orrandom. - Chronicle.Kotlin (Release v2.1.0): A
skipTlsValidationconnection string option to skip TLS certificate validation independently of disabling TLS entirely. Defaults totrue, matching the client’s previous behavior — setskipTlsValidation=falseto require full certificate chain validation. - Chronicle.Kotlin (Release v2.1.0): Client connections now report the process id, process path, machine name, and client type to the Chronicle kernel, improving connection diagnostics.
- Chronicle.Elixir (Release v2.1.0):
chronicle://host1:port,host2:port,...connection strings — the client now connects to any one of several configured hosts instead of only a single server. IPv6 hosts are supported via bracket notation. - Chronicle.Elixir (Release v2.1.0):
chronicle+srv://connection strings, resolving the target host(s) via a DNS SRV lookup, re-resolved on every connect/reconnect so membership changes are picked up automatically. The DNS server to query can be overridden with thesrvNameServeroption. - Chronicle.Elixir (Release v2.1.0): A
loadBalancerconnection string option selecting how one address is picked among several candidates:least-connections(the default — probes each candidate and picks the one with the fewest active connections),round-robin, orrandom. - Chronicle.Elixir (Release v2.1.0): A
skipTlsValidationconnection string option to skip TLS certificate validation independently of disabling TLS entirely. Defaults totrue, matching the client’s previous behavior — setskipTlsValidation=falseto require full certificate chain validation. - Chronicle.Elixir (Release v2.1.0): Client connections now report the process id, machine name, and client type to the Chronicle kernel, improving connection diagnostics.
- Arc (Release v20.61.0):
ARC0014analyzer diagnostic: a generic static method on a[ReadModel]whose return type matches a query shape is reported at compile time, since query discovery would register it as an endpoint that can never be invoked. It inspects internal methods as well as public ones, matching what discovery actually registers. - Arc (Release v20.61.0):
QueryMethodCannotBeGeneric, thrown byModelBoundQueryPerformer’s constructor, so a generic query method fails at wire-up naming the offending method rather than per request with a bare reflection message. - Arc (Release v20.60.2):
reconcileQueryDatain@cratis/arc/queries, which reconciles a freshly received payload against the previous one and preserves references for everything that did not change. Items are matched by their conventionalid, falling back to position. - Arc (Release v20.60.1):
ConceptValidator<T>rules are now generated into the client, so a concept declared once is validated in the browser everywhere it appears on a command property or query parameter - cli (Release v2.1.0):
cratis run [PATH]— boots a local Stage sandbox from the Screenplay (.play) files in a folder. It mounts the folder into thecratis/stagecontainer, publishes the Stage API onhttp://localhost:9090(override with--port), and streams the session until you stop it. Refuses to start when no.playfiles are present, and reports a clear error when Docker is unavailable. Choose the image tag with--tag. - Screenplay (Release v1.2.0):
IScreenplayPrinter/ScreenplayPrinter— renders a Screenplay syntax tree back to.playsource text, the reverse of the compiler. Overloads print a wholeApplicationSyntaxdocument as well as standaloneProjectionSyntax,SpecificationSyntaxandCaptureSyntax. A syntax tree can now be built or modified in code and printed to valid Screenplay, which is the basis for generating.playfiles from a model. - Screenplay (Release v1.2.0): Documentation: a “Printing and generating” reference page covering the printer, the round-trip guarantee with the compiler, and generating Screenplay from a tree built directly from a model.
Changed
Section titled “Changed”- Arc (Release v20.62.4): Updated Cratis Chronicle to 16.7.0
- Chronicle (Release v16.7.0): A
ReactorScenario<T>created without an explicitIServiceProvidernow builds one fromServices(standard dependency injection with logging registered) instead of usingDefaultServiceProvider. A reactor that relied onDefaultServiceProviderconstructing an unregistered concrete dependency must now register it. - Arc (Release v20.62.3): Updated Cratis Chronicle to 16.6.1
- Arc (Release v20.62.3): Updated System.Reactive to 7.0.0
- Arc (Release v20.62.3): Updated Entity Framework Core and other Microsoft/.NET dependencies to the latest 10.0.10 servicing releases
- Chronicle (Release v16.6.0): Read model change notifications now carry the change type and the causing event’s sequence number, occurred time and correlation id (#3359)
- Chronicle (Release v16.6.0): Faster reactor, reducer and event-type dispatch on the .NET client (#1309)
- release-action (Release v1.1.1): Release tags (
vX.Y.Zand the floatingvX/vX.Y) now point at a lean commit containing onlyaction.yml,dist,LICENSEandREADME.md, so auses:checkout is about 2.7 MB instead of 6.6 MB. - release-action (Release v1.1.0): The action now runs on Node 24.
- release-action (Release v1.1.0): The action now honors
GITHUB_API_URL, so it runs on GitHub Enterprise Server. - release-action (Release v1.1.0): A release created without notes of its own now uses GitHub’s generated release notes instead of an empty body.
- release-action (Release v1.1.0): An explicit
versionwithoutrelease-notesnow creates a release instead of being skipped. - AuthProxy (Release v2.9.0): The final
redirecttarget is now validated against the post-logout allow-list on both legs of the round-trip (initiation and callback), and is carried across the identity-provider round-trip in a short-lived HTTP-only cookie rather than in the URL. - AuthProxy (Release v2.9.0): OAuth 2.0 providers (such as GitHub) have no standard OIDC end-session endpoint and cannot be force-logged-out via a redirect; those sessions — and any request with no active OIDC session — fall back to the previous local-only logout (clear cookies + redirect). See
Documentation/configuration/logout.md. - AuthProxy (Release v2.7.0):
.cratis-tenantsnow persists for the post-login tenant switcher. It is written as a session cookie and is no longer deleted when a tenant is selected, so a user with more than one tenant keeps the list available for an in-app switch control for the rest of the browser session. Single-tenant users still have the cookie removed on auto-selection (no switcher), and every selection continues to re-validate membership against the tenants endpoint. - Chronicle.TypeScript (Release v2.1.1): When no OAuth2 token can be obtained, RPCs now proceed without authorization and fail with the server’s rejection instead of a client-side token fetch error
- Screenplay (Release v1.5.0): The full invoicing sample now exercises every language construct at least once: the
@sensitiveconcept attribute, the complete set of concept and command validation rules, claim-based and grouped authorization policies, a nested feature, file-based handlers, the full projection sub-language (sequence,every/all, composite keys,nestedblocks,remove via join,set/count/add/subtractmappings and the$eventSourceId/$causedBy/literal/template expressions), the remaining capture triggers (split, value transition, logicaland/or, template), screen titles/summaries/file references,htmlandtypescriptcode blocks, multiple reactor triggers and event-based specifications. - Chronicle.TypeScript (Release v2.1.0): Reconnect backoff is now jittered, so a fleet of clients that lost the same kernel does not reconnect in lockstep.
- Chronicle.TypeScript (Release v2.1.0): Updated to Chronicle contracts 16.4.0.
- Chronicle.Kotlin (Release v2.1.0): Reconnect backoff is jittered, so a fleet of clients that lost the same kernel does not reconnect in lockstep.
- Chronicle.Kotlin (Release v2.1.0): The client connection ID rotates on every reconnect, so observers re-register under an ID the kernel recognizes.
- Chronicle.Elixir (Release v2.1.0): The gRPC connection target now resolves any configured hostname correctly; previously only
localhostwas guaranteed to resolve. - Chronicle.Elixir (Release v2.1.0): Updated to Chronicle contracts 16.4.0.
- Arc (Release v20.61.0):
Cratis.Arc.ProxyGenerator.Buildtakes a framework reference onMicrosoft.AspNetCore.App. Reading validation rules loads the target project’s own assembly for real, and targets are ASP.NET applications, so that load fails without the shared framework. - Arc (Release v20.60.2): The
Connectedmessage on the observable query hub now carries the server’s keep-alive interval so clients can align their idle detection with it. - Arc (Release v20.60.2):
ObservableQueryHubMessage.CreateConnectednow takes the keep-alive interval alongside the connection identifier. This type is the hub’s wire protocol and is not constructed by application code, so no application-level change is needed. - Arc (Release v20.60.1): Query arguments are validated with the same traversal as command properties: a validator nested inside an argument, inside a collection, or on a concept now runs. Previously it only ran when the argument’s own type had a validator
- Arc (Release v20.60.1): Queries that carry a validator which previously never ran will now reject invalid arguments. Requests that succeeded before may return 400
- Arc (Release v20.60.1): Observable queries now run client-side validation, on both
perform()andsubscribe(). The rules were always generated but the proxy discarded them, so an observable query carrying a validator begins rejecting on upgrade — including asubscribe()started before its arguments are available, which anotEmptyrule will now reject. Gate such subscriptions withObservableQueryWhen. A rejected subscription delivers an invalid result to the subscriber instead of an empty one, so “these arguments are wrong” is distinguishable from “no data yet” - Arc (Release v20.60.1): Validation failures now report member names the way the client does — camelCased, and attributed to the field rather than to a concept’s inner value. A failure previously reported as
Emailoremail.Valueis nowemail. This applies to commands as well as queries, and is what lets a form match a server rejection to the field that caused it - Arc (Release v20.60.1): Query validators resolve from the request scope, so they can depend on scoped services
- Chronicle (Release v16.7.0):
ReactorScenario<T>now resolves reactor handler-method parameters — injected services and materialized read models — instead of failing to resolve them. - release-action (Release v1.1.2): A
workflow_dispatchrun left at the default0.0.0version no longer creates a bogusv0.0.0release or publishes0.0.0artifacts. An empty or0.0.0version now works the version out from the merged pull request as intended. - Chronicle (Release v16.6.1): The .NET client NuGet packages now build for the net8.0 and net9.0 targets again, so the packages publish with the release
- Chronicle (Release v16.6.0): Releasing a coarse
[PII]collection for an erased subject no longer crashes — it reads as an empty collection instead of throwing and potentially quarantining an observer after right-to-erasure - Chronicle (Release v16.6.0): Projection-backed observable read-model queries keep emitting after a connection drop, instead of silently freezing on stale data after the first reconnect
- Chronicle (Release v16.6.0): Appending a batch of events no longer risks an infinite retry loop that permanently wedges an event sequence, or a sequence-number gap and phantom tail when the storage append throws
- Chronicle (Release v16.6.0): The SQL event-store backend no longer risks an infinite retry loop when appending at a sequence number that is already taken
- Chronicle (Release v16.6.0): Username and email lookups on the SQL backend are now case-insensitive, matching the in-memory and MongoDB backends
- Chronicle (Release v16.6.0): Waiting for observers to catch up no longer reports success against an observer that has never handled any events; event and read-model read cursors are now disposed when an enumeration error occurs
- Chronicle (Release v16.6.0): Event seeding no longer permanently loses events when applying them to an event store’s namespaces fails partway through, and no longer silently skips events when an append fails
- Chronicle (Release v16.6.0): Adding a new generation to an existing event type now works on the MongoDB backend, and resolving an event type by a specific generation from the SQL database on a cold cache returns that generation’s schema
- Chronicle (Release v16.6.0): Job monitoring surfaces backend failures instead of returning an empty result
- Chronicle (Release v16.6.0): A unit of work whose commit fails is no longer leaked; optimistic concurrency is no longer silently disabled when a later same-source event carries no scope; and completing one unit no longer clears the current-unit reference of a different unit
- Chronicle (Release v16.6.0): Paging a read-model query with a
- release-action (Release v1.1.0): A pull request closed without being merged no longer produces a release, even when it has a release label (#2, #4).
- release-action (Release v1.1.0): Duplicate releases from the version being recalculated in the post step are no longer created (#103).
- release-action (Release v1.1.0): A merged pull request from a branch named after a version now produces a correct version.
- release-action (Release v1.1.0): The
isolated-for-pull-requestoutput was inverted and now reports correctly. - release-action (Release v1.1.0): Releases now point at the actual merge commit instead of the ephemeral pull-request merge commit.
- Arc (Release v20.62.2): Header-based tenant resolution now applies for the whole request instead of only inside endpoint delegates, so tenant-scoped work that runs earlier no longer falls back to the default namespace and database; header lookup is now case-insensitive so a configured header still matches when HTTP/2 delivers it lowercased (#2351)
- Arc (Release v20.62.2): An
IAsyncEnumerable-backed observable query is now stopped on unsubscribe, instead of leaking and duplicating results on re-subscribe - Arc (Release v20.62.2): Observable queries served through the MongoDB watcher are now isolated per tenant database instead of all sharing the first tenant’s
- Arc (Release v20.62.2): The MongoDB watcher now resumes from the last change after a transient reconnect instead of silently dropping changes that happened during the disconnect
- Arc (Release v20.62.2): A
float-backed concept can now be persisted to MongoDB — it previously threw on every write - Arc (Release v20.62.2): A
DateTime-backed concept now round-trips correctly instead of being read back as a wildly wrong date - Arc (Release v20.62.2): A command that returns an event collection containing a null element no longer fails with a server error during response detection
- Arc (Release v20.62.2): Injecting an aggregate root into a command’s
Handlenow resolves the aggregate root itself rather than aTask - Arc (Release v20.62.2): Executing a command with a base-type or interface result type now returns the result instead of throwing when the response is assignable to it
- Arc (Release v20.62.2): An exception thrown by an aggregate root event handler now surfaces as itself, instead of being wrapped in a reflection exception that hid its type and stack
- Arc (Release v20.62.2): An aggregate root commit result now reports only that aggregate’s own events, with sequence numbers that correspond to them
- Arc (Release v20.62.2): A command, query, or read model that references an enum not backed by
int(for exampleenum X : byteor[Flags] enum X : long) no longer breaks TypeScript proxy generation - Arc (Release v20.62.2): A
Uriproperty now generates a TypeScriptstringinstead of aDate - Arc (Release v20.62.2): A dictionary whose key or value is a package type such as
GuidorTimeSpannow generates compilable TypeScript, with the type’s import included - Arc (Release v20.62.2): Command and query validation no longer relies on
Expression.Compile,Activator.CreateInstanceover a runtime-constructed generic, or private-member reflection, so it works under NativeAOT and trimming (#2204) - AuthProxy (Release v2.10.1): Logout now clears the transient OAuth/OIDC correlation and nonce cookies (
.AspNetCore.Correlation.*,.AspNetCore.OpenIdConnect.Nonce.*) the sign-in handshake leaves behind on abandoned attempts, so signing out no longer leaves them accumulating in the browser. These cookies are now kept at the root path so the browser sends them on the logout request. - Arc (Release v20.62.1): Fixed a process crash in observable-query streaming (SSE and WebSocket) when a client disconnects while an emission is in flight. The emission callback is
async voidand, once the connection’s write lock, emission gate and cancellation source are disposed on disconnect, the in-flight emission threwObjectDisposedExceptionon a background thread, which went unhandled and terminated the whole process. Disconnecting mid-emission is now a graceful no-op: the expected cancellation/disposal/transport signals are swallowed and only genuine failures are surfaced to the subscriber. - Screenplay (Release v1.5.1): The invoicing sample no longer uses a redundant projection-level
automap— AutoMap is the default - Screenplay (Release v1.5.1): The invoicing sample uses
everyinstead ofallfor per-projection mappings, and showcasesallin a new SystemActivity slice matching its documented system-wide semantics - Screenplay (Release v1.5.1): The invoicing sample no longer points a projection at a named sequence its own events never reach
- Screenplay (Release v1.5.1): The invoicing sample maps arithmetic counters only from events that carry the referenced property, imports every external event it references, and uses the documented
=mapping and plain string literals - Screenplay (Release v1.5.1): The auto-map documentation no longer shows an event-level
no automapthe parser rejects, and its join and children examples use the actualwithandidentified byforms - Screenplay (Release v1.5.1): The projection grammar documents the per-
withAutoMap toggle and thesequencedirective, and its closing example uses the real join syntax - Chronicle.TypeScript (Release v2.1.1): Fixed the client dropping its Chronicle session when an OAuth2 token refresh failed while the cached token was still valid — the cached token now keeps flowing until it truly expires, and failed token fetches are throttled so an unreachable authentication endpoint no longer turns every RPC into a token request
- Chronicle.TypeScript (Release v2.1.1): Fixed a non-numeric
expires_inin the token response causing a token fetch on every RPC - Chronicle.Kotlin (Release v2.1.1): Fixed the client retrying a dead server address forever: the connection now redials on every reconnect attempt — re-resolving DNS/SRV records and re-selecting a host through the load balancer — so a replaced host or a changed record is picked up instead of being retried indefinitely
- Chronicle.Kotlin (Release v2.1.1): Fixed a momentary token-endpoint outage failing calls while the cached OAuth token was still valid: the client now falls back to the cached token until it truly expires and throttles failed fetch attempts, so an auth blip no longer drops the session
- Arc (Release v20.62.0): Header-based tenant resolution now applies for the whole request instead of only inside endpoint delegates, so tenant-scoped work that runs earlier no longer falls back to the default namespace and database; header lookup is now case-insensitive so a configured header still matches when HTTP/2 delivers it lowercased (#2351)
- Chronicle (Release v16.5.2): The SQL event-store backend no longer risks an infinite retry loop when appending at a sequence number that is already taken (for example after a silo restart leaves the grain’s counter behind the stored tail). Storage now reports the true next-available sequence number so the append recovers, matching the MongoDB and in-memory backends
- Chronicle (Release v16.5.1): Job monitoring no longer hides backend failures as an empty result. When the jobs storage fails, observing jobs now surfaces the error to the caller instead of an empty stream, and both observing jobs and reading job steps log the failure — so the Workbench no longer silently shows an empty list on a real fault
- Chronicle (Release v16.4.1): Projection-backed observable read-model queries now keep emitting after a connection drop — previously the watch stream was never re-established after the first reconnect, so live queries silently froze on stale data
- Chronicle.Elixir (Release v2.1.3): Fixed the hourly session drop for clients using username/password authentication: the OAuth2 token is now attached to every RPC individually — cached and refreshed ahead of expiry — instead of being baked into the gRPC channel at connect time, so token expiry no longer invalidates the channel or interrupts the session
- Chronicle.Elixir (Release v2.1.2): Fixed the client permanently losing its Chronicle session after a stale gRPC channel — e.g. an hourly OAuth token expiry or silent TCP death — by rebuilding the channel (re-resolving addresses and re-fetching authentication headers) whenever the session drops or fails to start, instead of retrying forever on the dead channel
- Arc (Release v20.61.4): A
Uriproperty now generates a TypeScriptstringinstead of aDate, so its value is no longer silently lost to an invalid date on the client - Chronicle.Elixir (Release v2.1.1): Fixed a race condition where starting a Chronicle connection session without an explicit client name could fail with
{:error, {:already_started, pid}}when done concurrently, because every unnamed session was registered under the same global process name. - Chronicle.Elixir (Release v2.1.1): [x]
mix compile --warnings-as-errors - Chronicle.Elixir (Release v2.1.1): [x]
mix test(full suite, run repeatedly — 0 failures) - Chronicle.Elixir (Release v2.1.1): [x] Targeted stress test: 50 concurrent
Session.start_link/1calls withoutclient_name— 0 collisions - Chronicle.TypeScript (Release v2.1.0): The client now reconnects when the kernel keep-alive is lost. Previously the keep-alive loop logged the stream ending or erroring and then stopped, leaving the client on a dead session: appending events kept working while reactors and reducers silently stopped receiving anything.
- Chronicle.TypeScript (Release v2.1.0): A connection that goes silent without erroring is now detected and reconnected. The kernel does not close the keep-alive stream when it evicts a client, so waiting for an error never fired.
- Chronicle.TypeScript (Release v2.1.0): Reactor and reducer observation streams are re-established after they end, instead of that observer stopping permanently. This includes streams the kernel closes deliberately, such as cross-store (inbox) observations.
- Chronicle.TypeScript (Release v2.1.0): Reconnects no longer leak a duplicate observation stream per cycle, or leave the re-established observation untracked.
- Chronicle.TypeScript (Release v2.1.0): The connection health check can no longer hang indefinitely or stack overlapping probes.
- Chronicle.Kotlin (Release v2.1.0): The client now reconnects to the kernel instead of staying down. A dropped connection was swallowed by empty catch blocks, so it completed silently with nothing logged and no way to recover short of restarting the application.
- Chronicle.Kotlin (Release v2.1.0): A connection that goes silent without erroring is now detected and reconnected. The kernel does not close the keep-alive stream when it evicts a client, so waiting for an error never fired.
- Chronicle.Kotlin (Release v2.1.0): Reactors and reducers are re-registered on every reconnect. They previously registered once against a connection ID captured at construction, so after any reconnect they observed nothing for the rest of the process lifetime.
- Chronicle.Kotlin (Release v2.1.0): Reactor and reducer observation streams are re-established after they end, instead of that observer stopping permanently. This includes streams the kernel closes deliberately, such as cross-store (inbox) observations.
- Chronicle.Kotlin (Release v2.1.0): Keep-alive and observation failures are now reported instead of being discarded silently.
- Chronicle.Elixir (Release v2.1.0): Reactors and reducers no longer stop receiving events about a minute after startup. The client consumed the kernel’s keep-alives but never answered them, so the kernel stopped counting it as connected and unsubscribed its observers — appending events kept working, which made the client look healthy.
- Chronicle.Elixir (Release v2.1.0): A lost connection is now detected and reconnected even when the stream stays open. The kernel does not close the keep-alive stream when it evicts a client, so a half-disconnected client previously stayed in that state until the application was restarted.
- Chronicle.Elixir (Release v2.1.0): A dropped connection no longer schedules two overlapping reconnects, and a connection that never came up no longer reports a disconnect to observers.
- Arc (Release v20.61.3): A regex validation rule whose pattern already escapes a slash —
Matches(@"^\d{2}\/\d{2}$")— generated a client validator that did not parse: the slash was escaped a second time, and the resulting\\/let the slash terminate the regex literal early. Slash escaping is now escape-aware, so a pattern’s own escapes pass through untouched and only a bare slash is escaped. - Arc (Release v20.61.3): An exact-length rule —
Length(4)— projected to the client aslength(0, 0), since the extractor read aLengthproperty thatExactLengthValidatordoes not have. It reads the validator’sMin(which carries the exact length) instead, solength(4, 4)is generated. - Arc (Release v20.61.2): A model-bound command’s client validation rules were taken from the wrong type. When a command’s own validator held no client-projectable rule,
ToCommandDescriptortreated the supplied-but-empty rule set as “not supplied” and re-extracted from theHandlemethod’s parameter — which for a model-bound command is the valueProvide()resolves, not the command. A command whoseProvide()returns a record carrying a validated concept (e.g. anEmail) then had that concept’s rules emitted on its own generated validator, bound to a field the command does not have — invalid TypeScript. “No rules supplied” (null) is now distinguished from “supplied, empty”, so the fallback runs only for the controller path it was written for. - Arc (Release v20.61.2): A regex rule was emitted as a quoted string, but the client-side
matchesrule takes aRegExp. The pattern is now carried through as aRegularExpressionPatternand formatted as a regex literal — an empty pattern as/(?:)/, a literal slash escaped — somatches(/^\d{4}$/)is generated rather than an unassignablematches('^\d{4}$'). - Arc (Release v20.61.1): A validation rule comparing against a non-numeric value — most often a
RuleFor(x => x.When).GreaterThan(DateOnly.MinValue)“must be set” sentinel — generated a client validator that did not parse (greaterThan(01/01/0001), an invalid TypeScript literal). The client-side comparison validators (greaterThan,lessThan, …) operate on numbers, so such a comparison is now skipped at extraction — the server still enforces it, there is simply no client rule to project. As a backstop, any non-numeric argument that still reaches the formatter is quoted, so no unparseable literal can be emitted. - Arc (Release v20.61.0): FluentValidation rules now reach generated client proxies. No rule had ever been projected: generation walks the target project through a
MetadataLoadContext, whose types carry no executable code, while a FluentValidation rule only exists once its validator’s constructor has run — so instantiating the validator always threw, and the catch turned that into “no rules”. Every extractor spec passed because they hand it runtime types, which the generator never has. Validator types are now resolved in the default load context and instantiated there. DataAnnotations were unaffected, being read from metadata. - Arc (Release v20.61.0): A validation message declared lazily —
.WithMessage(_ => Messages.Something), the form a localized message takes — is held as a factory rather than a string and was dropped. It is now resolved where it does not depend on the instance, and left unprojected rather than guessed at where it does. - Arc (Release v20.61.0): The proxy generator’s
AssemblyLoadContext.Defaultresolving hook handed back a metadata-only assembly, which throwsResolved assembly must be a runtime Assembly object. Caching that assembly is the point of the hook; returning it was not. - Arc (Release v20.61.0): A generic static method on a
[ReadModel]is no longer discovered as a query. Discovery matches on return type alone, so a generic composition helper returning the read model’s own query shape was registered, routed, and given a client proxy — and then every call failed withLate bound operations cannot be performed on types or methods for which ContainsGenericParameters is true. Rejected now at the shared runtime predicate, its copy in the proxy generator, and the metadata generator. - Arc (Release v20.60.3): A command implementing
ICanProvideEventSourceIdno longer returns an unhandled server error when its key cannot be composed from the incoming payload — an uncomposable or null key resolves toEventSourceId.Unspecified, so the command reaches the filter and validation stages that turn bad input into a proper response. - Arc (Release v20.60.2): Observable query connections no longer reconnect spuriously while data is flowing. The server now guarantees a message at least once per keep-alive interval instead of allowing gaps of up to twice the interval, so the SSE client stops tearing down healthy connections, re-subscribing every query and re-delivering every snapshot — which surfaced as UI flicker during normal use.
- Arc (Release v20.60.2): Re-delivering a snapshot no longer re-renders everything. Incoming payloads are reconciled against the data already held, so items that did not change keep their object identity and only the items that actually changed re-render. An identical snapshot produces no update at all.
- Arc (Release v20.60.2): Setting
Query.KeepAliveIntervalto a longer value no longer causes the SSE client to reconnect in a loop. The client now derives its idle threshold from the interval the server advertises rather than assuming the default. - Arc (Release v20.60.2): Disabling keep-alive by setting
Query.KeepAliveIntervalto zero or a negative value no longer leaves the SSE client reconnecting forever waiting for pings that were never going to come. - Arc (Release v20.60.1): A validator that throws while validating a query is reported as a validation failure rather than a server error
- Arc (Release v20.60.1): A query taking an injected dependency alongside its arguments now has its client-side rules generated; the shape check previously required the argument model to mirror the dependency too, and silently emitted no rules
- Arc (Release v20.60.1): A concept reachable both through a query’s argument model and through the parameter itself no longer has its rule, and its message, emitted twice
- Arc (Release v20.60.1): A query with no arguments no longer binds an unrelated same-named
Parameterstype - Arc (Release v20.60.1):
DateOnlyandTimeOnlyvalues are no longer walked as though their calendar components were part of the model - Arc (Release v20.60.1): Validation rules carrying a string argument (such as
Matches) or a message containing an apostrophe now generate valid TypeScript - Screenplay (Release v1.2.1): The printer no longer breaks the build of applications that use Cratis type discovery. The internal
ScreenplayWriter’s nestedScopestruct was public, so consumers’ generated type-discovery provider referenced it through its internal container and failed to compile (CS0122).Scopeis now internal.
Security
Section titled “Security”- AuthProxy (Release v2.10.0): The endpoint is a service-to-service back-channel that carries no bearer token (there is no user-supplied token in this flow); it relies on being network-isolated from browser traffic, exactly like the invite and link exchanges. It trusts the subject AuthProxy delivers from a real provider authentication, never a client-supplied subject.
- AuthProxy (Release v2.10.0): The client IP and derived location are personal data; the application is responsible for handling and retention.
- AuthProxy (Release v2.8.0): The link challenge requires an authenticated session (anonymous →
401), validates the provider scheme (unknown →404) and requires a one-time token (missing →400).returnUrlis constrained to a same-site relative path so the endpoint can never be turned into an open redirect. The subject is delivered to the application server-to-server from a real provider authentication — never trusted from client input. - AuthProxy (Release v2.8.0): Not an iframe. The design in the issue calls for an iframe, but provider consent pages send
X-Frame-Options: DENYand AuthProxy cookies areSameSite=Lax, so the flow is a popup / top-level redirect, as agreed in the issue’s posted design. - AuthProxy (Release v2.8.0): Session preservation is achieved by short-circuiting
OnTicketReceivedwithcontext.HandleResponse()before theRemoteAuthenticationHandlersigns the ticket into the cookie scheme (the “capturesub/issand short-circuit” option from the design), rather than a throwaway sign-in scheme. - AuthProxy (Release v2.8.0): Same-account limitation (assumption): the challenge does not force
prompt=select_account, so a provider with an active SSO session may silently return the same account. Left out because prompt handling differs across OIDC/OAuth and GitHub has no standard equivalent; flagging for review — it can be added if linking a different account of the same provider must be guaranteed. - Arc (Release v20.60.1): Validators declared against a query’s argument set are now enforced on the server. They were previously applied only by the generated
Deprecated
Section titled “Deprecated”- No updates