Repository navigation
Release v3.5.1
Applications on cratis_chronicle 3.5.0 can lose data silently: projections leave fields empty, reducers never run, constraints fail to register, uniqueness on multi-word fields isn't enforced, and seeding appends duplicate events on every start. This release fixes all of those. Every fix below was checked against a Chronicle 19.4.8 development kernel, as well as by specs.
Action required when upgrading
- Elixir 1.18 or later is now required. The package declared
~> 1.14, but everygrpc 0.11release depends ongoogleapis 0.1, which already required 1.18, so older versions could not compile the client. Nothing changes if your project already builds. - Check seeded event sources for duplicates. Under 3.5.0 seeders appended their events again on every start and reconnect. From 3.5.1 on, an event source that already has events is skipped, but duplicates already stored stay in the event log. Correct them with your own events if they matter.
- Check reducer read models and multi-word projection fields. Active reducers now register and process their events from the start of the log, passive ones compute when read, and projections now fill fields they left empty before. Both change what reads return, so check anything that relied on the empty values.
- Read model query pages count from 0.
Chronicle.ReadModels.query/2defaulted to page 1 and rejected 0, so it could never return the first page. It now defaults to 0. If you passedpage: 1for the first page, you were getting the second one; passpage: 0. - If you worked around the mapping defect with camelCase strings such as
set: [owner_name: "ownerName"], they keep working. You can go back toset: [owner_name: :owner_name].
Fixed
- Projections map multi-word event fields. Events are stored as camelCase JSON, but projection expressions sent atoms such as
:owner_nameas-is, so those fields were never set, with no error. Atom field references and key expressions are now sent as camelCase; a join'son:still names the read model property. Multi-word fields that the event and read model share are mapped automatically, following the kernel's own AutoMap rules: not for afromthat only aggregates, never over an explicit mapping or ano_auto_mapfield, and never from an event field a join's explicit mapping already reads. - Boolean and float constants in projections work.
set: [on_loan: true]was sent as a field name and never applied. It is now sent as a constant, like integers already were. - Reducers register. Every registration attempt failed with an
UndefinedFunctionErrorforCratis.Chronicle.Contracts.Observation.Reducers.SinkDefinition, so no reducer ever ran and its read model read as{:ok, nil}. - Constraints with
remove_constraintregister. The releasing event type was sent in a form the contract can't encode. Because constraints register before read models and projections, one such constraint stopped the whole client from reaching:registered. Several event types can now release the same constraint. - Uniqueness on multi-word fields is enforced.
unique(:owner_email)checked a property the stored events don't have, so duplicate values were accepted. has_events_for?/2answers correctly. It read the result from the wrong part of the kernel's response, so it always returned{:ok, false}, and seeding re-appended seed data every time the client registered.- Tail and next sequence number lookups return the real value. Without explicit filters they narrowed to the
"Default"source and stream type and returned{:ok, 0}. An omitted filter now includes every source and stream type. - Failed transport calls return errors. A call made while the channel was dying exited the calling process. A call the kernel rejected before replying, such as an unauthenticated one, raised a
KeyErrorinsidegrpc. Both now return{:error, %GRPC.RPCError{}}, and the response process the call had already started is stopped. Exceptions raised outsidegrpc, such as protobuf encoding errors, still raise. - The compatibility check sends the pinned contracts version. The package update to contracts 19.4.7 left the version it sends to the kernel at 19.4.0.
Changed
- The
:retry_attemptsoption is documented as accepted and ignored. Reconnection has never stopped after a number of attempts. - The documentation and console sample describe the fixed behavior, 22 shared Chronicle examples gain an Elixir tab, and several existing snippets that passed plain structs to read model functions now define real read models.
- Package updates now arrive as
no-releasepull requests, so the build and specs run on them before they reachmain.
Compatibility
Verified against a Chronicle 19.4.8 development kernel with cratis_chronicle_contracts 19.4.7, the version this release pins. The client resolves any contracts version, so pair it with a kernel that understands the contracts in your mix.lock. The published 3.5.1 package was also verified against a Chronicle 19.7.0 development kernel with contracts 19.7.0, which a fresh mix deps.get resolves as of 2026-09-25. A 19.6.1 kernel rejects contracts 19.7.0 with {:error, {:incompatible_server, ...}}; update the kernel and mix.lock together (Cratis/Chronicle#4213). Kernels 19.5 and 19.6 were not verified with their matching contracts.