Upgrade to 1.0
A Flow 1.0 upgrade includes binaries, event history, SQL schema, runtime-build routing, and task queues. Replacing a dependency version alone does not complete a durable upgrade. Before migrating a production database, confirm that its starting point is covered and prepare a tested recovery point.
Supported starting points
The automated history and SQL upgrade floor is v0.5.0. The 1.0 release gate resumes interrupted-step histories produced by v0.5.0 and by final pre-release v0.13.1. Qualification requires one deterministic redelivery and a new terminal event, not deserialization alone.
History or databases older than v0.5.0 are outside the automated contract. Do not open them directly with 1.0. Use an application export or a separately qualified staged migration.
SQLite baselines
PostgreSQL baselines
Patch releases in one row share the same migration prefix. Every published migration has a pinned SHA-256, so editing old migration content fails before production.
Code-level changes
Flow 1.0 requires Rust 1.88. Pin toolchain and dependency first.
Public enums and projection types generally use #[non_exhaustive]. Downstream matches need a fallback arm, and read-only snapshots should not be constructed through struct literals.
Keep explicit feature flags for SQL, Boot, and event bridging. Avoid changing storage backend, task backend, and workflow semantics during the same upgrade. Combining them expands the rollback boundary.
Pre-upgrade inventory
- List every non-terminal run and its
runtime_build_id. - Count legacy runs without a pinned build.
- Count waits, delayed retries, active hooks, and running steps.
- Record queue depth, active leases, and oldest task age.
- Preserve the current binary, lockfile, migration ledger, and local durable-directory version.
- Create a database recovery point and restore it in isolation.
If active history remains, retain the old workflow code and step handlers needed to replay it. Successful deserialization does not prove safe replay.
Configure runtime-build admission
The 1.0 engine should declare its current build and each old build proven replayable.
Use accept_unpinned() only while draining history created before build pinning. Define an end condition and remove it after those runs terminate.
If the 1.0 binary does not include the complete decision logic required by an old build, do not advertise it as compatible. Keep an old worker and dispatch through exact RuntimeBuildTaskRouter routes.
Quiesce writes
Stop new runs and scheduler submission before migration.
- Stop the old SQLite owner and confirm no process holds the file.
- Let PostgreSQL transactions finish, then let a dedicated migration job acquire the advisory lock.
- Make callback endpoints return a retriable response or place authenticated requests in a durable ingress queue.
- Back up local JSONL history and audit logs at the same recovery point as the database.
Do not restart an old binary against the database after the 1.0 migration commits.
Apply migrations
SQLite uses transactional migration in SqliteEventStore::connect(). Production PostgreSQL uses a dedicated migration executor.
If migration fails, the attempted schema changes and migration record roll back in one transaction while event history remains. Inspect ledger and history, fix the cause, then retry with the same candidate.
Serving workers use PostgresEventStore::connect_verified() or from_executor_verified(). Verification failure must keep the instance out of readiness.
Verify after startup
Complete these checks in order before restoring full traffic.
- Match every expected ID and checksum in
a3s_orm_migrations. - Read representative terminal, waiting, hook, retry, child, and continuation histories.
- Compare run IDs, last event sequences, and status distribution with the pre-migration inventory.
- Resume one interrupted-step canary and verify one redelivery with external idempotency.
- Resume one due wait and verify one completion event.
- Restore starts and scheduling gradually while watching store errors and backlog.
Keep old-build routes until their active history reaches terminal state. After unpinned runs drain, remove accept_unpinned() and test the rejected-admission path again.
Rollback boundary
Before the 1.0 migration commits, an operator may stop the candidate and reconnect the old binary to unchanged schema.
After commit, binary-only rollback is unsupported. The migration ledger is append-only. An old release does not recognize the 1.0 migration ID and correctly refuses the database. Do not delete migration records, projection tables, or triggers to disguise the schema.
Rollback after commit requires full restoration.
- Stop every 1.0 and old writer.
- Restore the verified pre-upgrade database point and matching local durable files.
- Deploy the original binary and workflow code.
- Verify history sequences, run states, and queue ownership.
- Restore external traffic.
Local qualification commands
Run the PostgreSQL command against an isolated temporary database, never a production instance.
