A migration reuses everything you already know about
effects. What differs is where you declare it: its
own
migrations map, not actions.What you write, and what you don’t
This is the whole contract on the configuration side:
You never write an
initActions entry, a group grant, a transitions entry,
hidden: true, or a “mark as applied” call. If you find yourself adding any of
those, something is wrong.
1. Declare the migration
- API
- Type
string
required
Human-readable label, shown in the run history and in the trace timeline.
ArchiveCondition<TState>
required
Which traces the migration applies to. Non-empty by type: an empty filter would
target every trace in the workflow, so it does not compile.
IEffectDef[]
required
The data transformation, exactly as in a normal action.
string
Icon shown alongside the migration.
string
Longer explanation of what the migration does.
string
Defaults to the key you register the migration under.
Writing the filter
filters is the same shape as a workflow’s
archiveCondition, so field is typed
against your state: a misspelled field name is a compile error.
type:
Traces where the field is absent are never selected, so you do not have to guard
against missing data in the filter.
Writing the effect
The effect body contains only the transformation. No bookkeeping.If assigning to the field is a compile error, the field is read-only because it
reaches your state type only through Migrate a field your state type declares explicitly instead, or widen the field in
the state type first. Do not reach for a cast — the error is telling you the type
says this data never changes, which is worth fixing rather than silencing.
initialStateData, which is declared
as const. typeof initialStateData then types it as a readonly tuple of literals,
which no effect can assign to:2. Register it in migrations
In the workflow’s config, beside actions — not inside it:
pastaLaVista/index.ts
Keeping migrations out of
actions is deliberate. It means a grant built by mapping
over every action (initActions: { kitchenTeam: allActions }) cannot pick a
migration up. Config assembly strips them from initActions as well, so you have
nothing to remember either way: a migration is never offered to a user because it is
never in anyone’s next actions.3. Declare the traceBot team
Every migration link is authored by the group labelled traceBot. Declare it like
any other team, in the same organization as your workflow:
account/teams.ts
4. Deploy, then hand over
Deploy the configuration as usual. From then on it is an administrator’s job to run the migration; nothing further is needed from the configuration project. They will always dry-run first, which reports how many traces match and changes nothing.Re-running a migration
When a migration is applied to a trace, the platform stamps it — in its own field on the trace’s state, besidedata, never inside it:
- running the same migration twice is a no-op
- if some traces fail, re-running retries only those
- each migration is stamped under its own key, so migrations never hide each other
data is what makes it survive an action that replaces data
wholesale.
Should you ever need to branch on it, it is typed on the state:
One migration, one file, one key
A migration that has run is finished. Never edit it. Once a migration has run anywhere, treat its file as closed. Need a different transformation, a corrected filter, or a second pass over the same field? Add a new migration: a new file, a new key, a new entry inmigrations.
Keeping old migration files costs nothing. A migration that has already run selects
no traces, so leaving it in
migrations is free — and it is the record of what was
done to the data.How a migration appears to users
- Business users never see it. It is not in anyone’s next actions, so it is never offered, and its links are hidden from the trace history.
- Administrators can reveal the links with a toggle in the trace header, where they are marked distinctly so they cannot be mistaken for business events.
- The run itself is visible in the workflow’s batch action history, to administrators only, with progress and any failures.
Checklist
Before asking for a migration to be run:- it is a new file under a new key — no migration that has already run was edited
-
filtersselects only the traces you mean — check the count with a dry run - the effect returns early when there is nothing to change
- the migration is registered in
config.migrations - a
traceBotteam exists, keyedtraceBot, withincludeSuperUser: true - the configuration has been deployed
Effects
The business logic a migration reuses
Action definition
Everything else an action can declare
State
The data a migration changes
Accounts
Teams, users and organizations