Macro for defining versioned checkpoint schemas with migration support.
Checkpoint schemas declare the current version and provide migrate/2
callbacks that transform state from older versions to the current one.
Usage
defmodule MyApp.Checkpoints.POInvestigation do
use Cyclium.CheckpointSchema, version: 2
def migrate(1, state) do
contacts = (state["vendor_contacts"] || [])
|> Enum.group_by(& &1["vendor_id"])
{:ok, Map.put(state, "vendor_contacts", contacts)}
end
def migrate(2, state), do: {:ok, state}
def migrate(_v, _state), do: {:error, :unsupported_version}
endRegistration
Declare the schema on the expectation (preferred — keeps it next to the strategy whose state it versions):
expectation(:investigate_po,
strategy: MyApp.Strategies.POInvestigation,
checkpoint_schema: MyApp.Checkpoints.POInvestigation,
...
)Or register in app config, which takes precedence over the expectation declaration (useful as a deploy-time override):
config :cyclium, :checkpoint_schemas, %{
{"my_actor", "investigate_po"} => MyApp.Checkpoints.POInvestigation
}Guidelines
- Store IDs and refs, not full payloads
- Keep state flat — avoid deeply nested structures
- No raw tool responses in checkpoints
- Normalize early before checkpointing
Summary
Functions
Resolve the checkpoint schema module for an actor/expectation pair.
Functions
Resolve the checkpoint schema module for an actor/expectation pair.
Precedence:
- App-config override —
config :cyclium, :checkpoint_schemas, keyed by{actor_id, expectation_id}oractor_id(matched against the raw episode values, which are strings for DB-loaded episodes) - Expectation-declared schema (
checkpoint_schema: MyModule), registered in persistent_term when the actor boots
Returns nil when no schema is registered. Used by both the checkpoint
write path (version stamping) and the restore path (migration) — they must
agree on the schema or migration chains break.