Remote Migration and CI Drift¶
Page Maps¶
graph LR
family["Reproducible Research"]
program["Deep Dive DVC"]
section["Recovery Scale Incident Survival"]
page["Remote Migration and CI Drift"]
capstone["Capstone evidence"]
family --> program --> section --> page
page -.applies in.-> capstone
flowchart LR
orient["Orient on the page map"] --> read["Read the main claim and examples"]
read --> inspect["Inspect the related code, proof, or capstone surface"]
inspect --> verify["Run or review the verification path"]
verify --> apply["Apply the idea back to the module and capstone"]
A remote migration changes the place from which recorded state is expected to survive. A CI update changes the executor expected to prove that survival. Both changes can make a repository look healthy while weakening recovery.
This page keeps the two contracts separate:
- content continuity — every protected DVC object can be restored from the candidate storage
- executor continuity — the recovery check still runs under a known environment and produces interpretable evidence
A safe cutover needs both.
Treat migration as a change of authority¶
Adding a second DVC remote is configuration. Making it the recovery authority is a governance decision.
Before cutover:
flowchart LR
git["Git metadata"]
legacy["legacy remote<br/>authoritative"]
candidate["candidate remote<br/>under evaluation"]
reviewer["cache-empty reviewer"]
git --> legacy
git --> candidate
legacy --> reviewer
candidate -.not trusted yet.-> reviewer
After accepted cutover:
flowchart LR
git["Git metadata"]
legacy["legacy remote<br/>rollback or retirement"]
candidate["candidate remote<br/>authoritative"]
reviewer["cache-empty reviewer"]
git --> candidate --> reviewer
legacy -.retained until exit gate.-> candidate
The arrow changes only after evidence shows that the candidate satisfies the same protected-state promise.
Define the cutover contract before copying¶
A migration contract should name:
| Contract field | Review question |
|---|---|
| source remote | Which location is authoritative before cutover? |
| candidate remote | Which exact location and configuration are under test? |
| protected revision set | Which current and historical states must move? |
| expected identities | How will restored content be recognized? |
| permitted exclusions | Which expired states are intentionally left behind? |
| access identities | Which learner, CI, and recovery roles must read the candidate? |
| recovery objectives | How much state and time may be lost? |
| rollback boundary | Until when can the source resume authority? |
| retirement gate | Which evidence permits decommissioning the source? |
Writing this after the copy makes it easy to define success around whatever happened to move. Write it first so the migration can fail honestly.
Keep migration and verification permissions distinct¶
Migration needs write permission to candidate storage. Independent verification should need read permission only.
| Route | Legacy access | Candidate access | Purpose |
|---|---|---|---|
| inventory | read | none | identify promised source state |
| copy | read | write | populate candidate storage |
| verification | none or read for comparison | read | test candidate without repairing it |
| rollback | read | optional | restore prior authority |
| retirement | governed deletion | read | end source obligation after approval |
If the verification route includes dvc push, it can fill a missing object from the
operator's cache. A passing result then means "the candidate was repairable," not "the
candidate was complete at the start of review."
This permission separation is one of the strongest ways to prevent a migration check from creating its own proof.
Use gates that can stop the cutover¶
A durable migration has decision gates, not one long command transcript.
Contract gate¶
Required evidence:
- retention policy identifies protected artifact families
- every protected family maps to Git revisions or explicit object identities
- exclusions have closed obligations and recorded authority
- RPO and RTO are declared
Block when scope is unknown.
Candidate population gate¶
Required evidence:
- candidate remote configuration is reviewable
- copy receipts name the revision scope used
- copy failures and skipped revisions remain visible
- source remote stays authoritative
Block when copy output is incomplete or ambiguous.
Independent recovery gate¶
Required evidence:
- clean Git reviewers can resolve every protected revision
- reviewer artifact caches begin empty
- candidate access uses the intended recovery identity
- every artifact restores and matches its contract
- observed durations satisfy the objective
- no verification command mutates candidate storage
Block on any protected revision failure.
Authority gate¶
Required evidence:
- the default or documented recovery route points to the candidate
- CI and learner setup use the new authority
- monitoring and ownership transfer are complete
- rollback remains available for a stated interval
Block when configuration changed but users still depend on the source implicitly.
Source exit gate¶
Required evidence:
- the rollback interval closed without unresolved failures
- final candidate audit passes
- retention or archival obligations for the source are resolved
- retirement approval names the exact storage target
- decommission evidence will be retained
Block when deleting the source would erase the only known copy of any promised state.
These gates turn "migration finished" into several smaller claims with different evidence and permissions.
Copy scope must match retention scope¶
Common scope mismatches:
| Copy choice | Hidden loss |
|---|---|
| current workspace only | protected releases and historical rollback states |
| all Git commits only | selected DVC experiments that carry open decisions |
| all objects in storage | cost and clutter from expired state; unclear ownership |
| release tags only | current mainline continuity |
| one successful target | other artifact families in the same protected release |
There is no single widest flag that expresses every organization's policy. The migration plan must map obligations to branch, tag, commit, experiment, and artifact scopes deliberately.
Verify object identity after transfer¶
Storage-provider copy counts can say that objects moved. They do not necessarily say that the repository can resolve and use them.
Review from the repository boundary:
- clone the Git repository without the author's DVC cache
- select one protected revision
- remove any materialized artifact and local object cache
- pull the named DVC target from the candidate remote
- compare restored digest, schema, row count, or release verifier with the contract
- preserve duration and command receipts
- repeat in a separate reviewer for each protected revision
The separate reviewer prevents the first restoration from populating cache for later checks.
The course audit performs this pattern:
Read CURRENT_ONLY_COPY and RELEASE_ONLY_COPY together. Each candidate remote contains
real data and passes one restore. Each still blocks cutover because the protected set is
incomplete.
Plan rollback as a return of authority¶
"The old bucket still exists" is not a rollback plan.
A rollback plan states:
- the event that triggers rollback
- who can decide
- how default remote configuration returns to the source
- which writes occurred after candidate cutover
- how those writes will be reconciled
- how users learn which remote is authoritative
- which verification reruns after the return
Dual writing complicates rollback. If both remotes receive new objects after cutover, their coverage can diverge. Name whether the migration uses:
- source-authoritative copying, where the candidate is read-only to users until cutover
- dual publication, where both remotes receive governed writes for a bounded interval
- candidate-authoritative operation with source frozen as rollback evidence
The model must be explicit. Otherwise rollback can restore an older but incomplete view.
CI drift can invalidate the recovery witness¶
A scheduled recovery job is useful only if its executor remains understood.
CI drift can change:
- DVC command behavior and supported flags
- remote protocol dependencies
- certificate and credential handling
- filesystem link behavior in the cache
- Git clone depth and visible references
- dependency installation time
- artifact transfer performance
- checksum or schema tooling
A green job after an executor update may still test a narrower revision set. A red job may indicate an access or tooling change rather than missing content. Preserve enough runtime evidence to distinguish those causes.
Record the executor contract beside recovery evidence¶
At minimum, a recovery receipt should make these facts recoverable:
| Surface | Example evidence |
|---|---|
| workflow identity | CI configuration revision and job name |
| operating environment | image digest or runner image identifier |
| Python runtime | executable path and version |
| DVC runtime | dvc version output |
| Git behavior | version, clone depth, fetched refs |
| remote identity | DVC remote name, not secret-bearing URL output |
| credential role | stable role name or identity class, never token contents |
| timing | start, finish, and measured restore duration |
| result contract | revision identities and verifier output |
Secrets are not evidence to archive. Preserve the name and permission boundary of the credential, not its value.
Decide whether executor drift changes meaning¶
Use a controlled comparison:
| Old executor | New executor | Interpretation |
|---|---|---|
| same protected set, same restored identities, similar duration | same | recovery evidence remains comparable |
| same identities, slower than RTO | content continuity holds; time objective fails | |
| fewer Git refs visible | new job tests a narrower promise | |
| pull fails with authentication error | access continuity fails; content coverage remains unknown | |
| pull succeeds, verifier differs | runtime or semantic drift needs investigation | |
| DVC flag no longer exists | runbook and pinned tool contract are stale |
Do not normalize a new result merely because the CI update was planned.
Upgrade the executor without losing the baseline¶
For a CI image or DVC upgrade:
- capture the last accepted recovery bundle from the old executor
- run the new executor against the same protected contract
- compare resolved revisions, restored identities, durations, and failure classes
- explain every material difference
- accept the new executor only after the comparison is reviewed
- keep the evidence that connects the old and new witness
Pinning can make comparisons easier, but permanent pinning is not the objective. Controlled change is.
Diagnose migration failures precisely¶
| Symptom | First boundary to inspect | Do not conclude yet |
|---|---|---|
| revision checkout fails | Git reference coverage | candidate DVC object is missing |
| pull reports missing object | candidate object coverage and pointer identity | all candidate history is incomplete |
| pull reports permission error | recovery identity and remote access | object does not exist |
| digest mismatch after pull | contract freshness, object identity, verifier | transfer alone proved correctness |
| restore exceeds RTO | transfer path, object size, concurrency, executor | data is unrecoverable |
| old CI passes, new CI fails | executor comparison | storage migration failed |
Name the boundary before repair. That keeps the failed evidence useful.
Write the cutover note¶
An acceptable note answers:
- Which source and candidate remotes were compared?
- Which protected revisions and artifact families were in scope?
- Which states were intentionally excluded, under what authority?
- Did empty-cache, read-only restores match identity and time objectives?
- Which CI executor produced the evidence?
- What rollback interval remains?
- Who approved authority transfer?
- What exact evidence permits or blocks source retirement?
"Changed the DVC URL and tests pass" answers none of the hard questions.
Review checkpoint¶
You are ready to continue when you can:
- distinguish candidate configuration from recovery authority
- define contract, population, recovery, authority, and source-exit gates
- keep migration writes separate from read-only verification
- map retention scope to migration scope
- verify content identity from independent empty-cache reviewers
- describe rollback as a controlled return of authority
- record enough CI executor evidence to interpret drift
- separate missing content, missing access, missing Git refs, identity mismatch, and RTO failure
Migration preserves trust only when storage authority and its verification executor change under explicit, reversible decisions.