Migration framework
Documentation/Native architecture and formats
Migration framework4 min read

Migration framework

On this page

Operators use one entry point: scribe-server migrate --data-dir DIR. It preflights every catalog repository, holds the exclusive data-root lock for the whole run, and applies the ordered chain to the current build. --dry-run reports the plan without recovery or writes. See the operator procedure and boundaries.

The coordinator is crates/scribe-server-cli/src/migrate/runner.rs. It first converts atomic-publication-v1 lock transfers (crates/scribe-metadata/src/repo/migrations/publication_v2.rs), then runs the schema-3 branch-table and snapshot verification step, then registered metadata steps in crates/scribe-metadata/src/repo/migrations/. Descriptors advance last. A root can contain repositories at different progress points after interruption; rerunning completes each one's remaining work. No startup path migrates data.

Every conversion lives in one of two folders: database work in crates/scribe-metadata/src/repo/migrations/ (one file per step, tests in a sibling folder), and any descriptor handling it needs in crates/scribe-server-cli/src/migrate/. Numbered steps register in STEPS; layout upgrades that must finish before a repository can open at all, such as publication_v2.rs, are called directly by the runner ahead of them.

The registered steps are revision 1 (workspace-state-v1), which builds the workspace lifecycle indexes; revision 2 (path-fold-v1), which verifies the Unicode path collision profile and shared snapshots; and revision 3 (change-notes-v1), which creates the change-description journal. Shared snapshot profile 3 is a prerequisite; pre-snapshot development generations require their matching build and are refused before conversion.

Repository schema and migration revision are separate: schema describes the base layout; the revision records completed additive upgrades. New repositories create the final tables and current revision directly. The journal bytes are specified in the on-disk format. This framework currently migrates storage repositories. Gateway roots retain their own version checks, and this command does not invent conversions for their data or for arbitrary incompatible development layouts. Auth data directories have their own explicit command, scribe-auth migrate --data-dir DIR [--dry-run], implemented in crates/scribe-auth/src/store/migrate.rs with the same rules: it holds the directory's exclusive lock, refuses malformed records before writing, commits converted records with immediate durability before publishing the new schema marker by rename, and enumerates its crash windows for SCRIBE_FAULT. See upgrading the auth data directory. The web stack's database has its own explicit migrate command and data_migration ledger, run from the worker image; startup refuses data that needs a migration. See stored data versions.

Adding a step

  1. Append a named step with the next consecutive revision to STEPS and advance REPOSITORY_MIGRATION_VERSION. Never reuse a completed revision or change a published cursor's meaning. Describe the exact supported source layout and canonical destination bytes. Refuse ambiguity and future versions.
  2. Implement read-only start, prepare, and verify callbacks. start prepares initialization and the first cursor. prepare reads a bounded page and returns owned writes plus the next cursor; returning None asks the runner to verify and complete. Never hold read guards across a write commit.
  3. Keep write closures bounded and limited to prepared database mutations: no hashing, external I/O, authentication, or unbounded scans. The runner commits data and cursor together with Immediate durability. Verification runs outside a write transaction and must pass before the revision marker advances. finish performs only bounded marker writes in that final transaction. Workspace ownership lookups use the read-only storage auth claims outside the write transaction; they never make a network request.
  4. Preserve authoritative state and operation outcomes. Existing malformed data must fail verification; migration is not repair. Teach completed-version validation about required tables, and update fresh repository creation.
  5. Test empty and populated stores, multiple pages, reopen/resume at each checkpoint, aborted writes, unchanged source history, repeated completion, corrupt/future markers, and byte-identical dry runs. Add a maintenance CLI regression and update the format specification and operator documentation.

All calls to advance_migration require the caller to retain exclusive root ownership, including preparation and final verification. Future object-layout changes must use durable generation activation with file and directory flushes; database transactions alone do not make external object writes durable.

The metadata crate's fault-injection feature exposes migration crash windows through SCRIBE_FAULT=<point>=crash. Points are enumerated by the transaction implementation in migrations/fault.rs; the all-features tests terminate a child process at each window and reopen and finish its real database. Production builds omit environment injection. The server CLI forwards this feature only in explicit fault-injection builds.

Source docs/migrations.mdSnapshot 2b8dcccb