1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! Idempotent migration runner for on-disk store-shape changes.
//!
//! Why this exists: the multi-domain + multi-method rollout (see
//! `docs/multi-domain-spec.md` §6.5 and `docs/multi-method-hosting-spec.md`
//! §10) introduces breaking shape changes on first boot at the new version.
//! Existing webvh deployments need their DID records and ACL entries
//! migrated in place, exactly once, in a known order. A purpose-built
//! runner is required because (a) the workspace has no existing migration
//! framework, and (b) we need each migration to be both **idempotent**
//! (safe to re-run on a partially migrated store) and **monotonic** in a
//! defined order (multi-domain `M-02` writes the `domain` field that
//! multi-method `M-01` depends on).
//!
//! ## Contract
//!
//! Each migration implements the [`Migration`] trait — a stable id and an
//! async `run`. The runner tracks applied ids in the `meta` keyspace under
//! `migration:applied:{id}`; a migration whose marker is already present
//! is skipped. A migration that errors does **not** get marked, so the
//! next boot re-runs it from the top.
//!
//! ## Ordering
//!
//! The runner walks migrations in the order they were registered. Callers
//! are expected to register in dependency order. There is no DAG; if a
//! migration B depends on A having committed its writes, register A first.
//!
//! ## What this module ships
//!
//! This is the **skeleton** (T2): the trait, the runner, the per-id
//! marker logic, an empty-set test, and a fail-fast test. The first
//! actual migrations (`M-01` legacy `did_log` → `DidRecord` wrap, `M-02`
//! domain tagging) land in subsequent tasks and register here.
use Future;
use Pin;
use Arc;
use ;
use AppError;
use ;
pub use M01TagDidRecordsWithDomain;
pub use M02CacheDidRecordServices;
pub use ;
/// Boxed future used by [`Migration::run`] so the trait stays object-safe
/// without pulling in `async-trait`. Matches the pattern used by the
/// storage backends in `super::store`.
pub type MigrationFuture<'a> = ;
/// Key prefix inside the `meta` keyspace for applied-migration markers.
const APPLIED_KEY_PREFIX: &str = "migration:applied:";
/// One on-disk shape change.
///
/// Implementations are expected to be:
/// - **Idempotent.** Re-running the same migration on a fully migrated
/// store must be a no-op (or a near-no-op walk that finds nothing to
/// change). The runner skips via the applied-marker; idempotency
/// inside `run` protects against partial-application reruns.
/// - **Deterministic on success.** No reliance on wall clock, hostname,
/// or random state that would make two replicas drift if both run the
/// migration concurrently.
/// - **Crash-safe.** A crash mid-migration leaves the store in a
/// re-runnable state. Use [`Store::batch`] to commit groups of
/// related writes atomically.
/// Persisted marker written under `meta:migration:applied:{id}` after a
/// migration completes. The `applied_at` timestamp is informational; the
/// presence of the marker is what gates re-runs.
pub
/// Helper: open the `meta` keyspace. The keyspace name will move to the
/// centralised registry in T3; until then it lives next to its only
/// consumer.
pub
/// Convenience: register migrations as an ordered list. Lives here so the
/// per-binary boot path can hand the runner a slice without juggling
/// `Arc`s. The runner itself only reads in order.