Versioning and deploys
Every run is stamped with its workflow’s version when it is submitted. By default a run is pinned: only a worker whose registered version matches will claim it.
The default version is a 12-character hash of the function body. Any edit to the body changes it. Deploy that edit, stop the old workers, and runs started under the old code sit unclaimed: still queued, not lost.
Choosing a policy
Section titled “Choosing a policy”from pyrula.workflows import workflow
@workflow(name="charge", version="2026-10-01", version_behavior="auto_upgrade")async def charge(ctx, amount_cents: int) -> str: ...pinned (default) | auto_upgrade | |
|---|---|---|
| Who claims an old run | only a worker on the same version | any worker |
| On a newer worker | n/a | shadow-replays the run’s history against the new code first; adopts it if compatible, else ends it with run:error (incompatible_upgrade) |
| Use when | a change can’t be replayed safely | changes are additive or guarded with ctx.patched |
Set version= explicitly so cosmetic edits (a comment, a log line) don’t fork the
version. Change it when the behavior changes.
@agent takes the same version= and version_behavior=.
Shipping a change to a pinned workflow
Section titled “Shipping a change to a pinned workflow”-
Deploy new workers alongside the old ones. New runs get the new version; old workers keep finishing old runs.
-
Wait for the old version to drain, then retire it:
from pyrula.workflows.versioning import retire_version, wait_drainedif wait_drained(store, "charge", old_version, timeout=3600):retire_version(store, "charge", old_version) # refuses while runs are in flight -
Stop the old workers.
A worker that finds runs it can’t serve logs them and keeps waiting. To fail them after a
deadline instead, set WorkerOptions(stranded_policy=StrandedPolicy.FAIL, stranded_timeout=...); they end with no_compatible_worker.
Proving a change is safe
Section titled “Proving a change is safe”Before deploying an auto_upgrade change, replay real histories against the new code in
CI with export_run_history and assert_replay_compatible. See
Deploy-safe versioning.