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
- Append a named step with the next consecutive revision to
STEPSand advanceREPOSITORY_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. - Implement read-only
start,prepare, andverifycallbacks.startprepares initialization and the first cursor.preparereads a bounded page and returns owned writes plus the next cursor; returningNoneasks the runner to verify and complete. Never hold read guards across a write commit. - 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.
finishperforms 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. - 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.
- 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.