Add migration runner — atomic_agents.migrate (closes #4)#15
Closed
dep0we wants to merge 1 commit into
Closed
Conversation
Implements the schema migration runner per spec/03-file-formats.
The highest-stakes module: bad migration corrupts vaults. Snapshot before,
dry-run preview, validate after, atomic rollback on failure.
Key pieces:
- MigrationScript Protocol — each vN_to_vM.py script in
<agents_root>/_migrations/ implements:
FROM_VERSION, TO_VERSION (validated against filename pattern)
applies_to(path) -> bool
migrate(path, dry_run) -> dict
- discover_scripts() — finds scripts, validates filename ↔ module version
consistency, rejects gaps (v1→v3 skipping v2), sorts by version chain
- find_content_files() — walks <agents_root>/<agent>/{memory,wiki}/*.md;
excludes _dashboard, _migrations, _cache, .git, INDEX.md
- get_current_vault_version() — reads schema_version from a sample of
files; returns lowest if mixed (treat as needing migration up)
- build_migration_plan() — chains scripts from current → target;
rejects gaps in the chain
- create_snapshot() — tar+gzip of vault to
_migrations/snapshots/YYYY-MM-DD_pre_vN_migration.tar.gz; excludes
meta dirs and log JSONLs (regenerable)
- restore_snapshot() — clears current vault content, extracts snapshot
- run_migration() — orchestrator with mandatory snapshot, per-script
application, post-migration validation, automatic rollback on:
- Validation failure (any file's frontmatter doesn't pass new schema)
- Catastrophic exception during application
- vault_status() — current version, available scripts, snapshots
- list_snapshots() — newest-first
- CLI: --to vN, --dry-run, --status, --rollback, --list-snapshots
HARD RULES enforced (per spec/03):
- Forward-only (rejects target ≤ current)
- Multi-agent atomicity (any file failure rolls back all changes)
- Snapshot ALWAYS taken before non-dry-run migration
- Validation runs after every migration; rollback on failure
- Rollback failure (when both migration AND rollback fail) leaves vault
in inconsistent state; explicit error guides operator to manual recovery
- Custom user-added frontmatter fields preserved (frontmatter library
round-trips unknown fields automatically)
Important safety design: the helper's CURRENT_SCHEMA_VERSION stays at
the version it can handle; until a migration AND a helper bump land
together, post-migration validation correctly rejects the migrated
files and rolls back. This is intended — you can't migrate to a
version the helper doesn't support yet.
Tests: 32 new tests covering script discovery (positive + 4 error cases:
version skip, version mismatch, missing required attribute, underscore-
prefix exclusion), content file walking (incl. excluded dirs), version
detection (with mixed versions returning lowest), plan building (chain
discovery + 2 error cases), snapshot creation/restore (round-trip with
mutation in between), snapshot listing (newest first), end-to-end
migration (dry-run preserves files; real migration with validation
rollback proves the safety mechanism works), file-touched recording.
Total suite: 190/190 passing.
This was referenced May 7, 2026
Owner
Author
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements issue #4 — schema migration runner per spec/03-file-formats. The highest-stakes module in the stack — bad migration corrupts vaults. Snapshot before, dry-run preview, validate after, atomic rollback on failure.
Design
The defensive architecture is what's load-bearing here:
_migrations/snapshots/YYYY-MM-DD_pre_vN_migration.tar.gz.dry_runand don't write when set.Forward-only by design (refuses target ≤ current). Forbids gaps in the migration chain (refuses v1→v3 if v2 isn't reachable through v1→v2).
Important safety property
The helper's
CURRENT_SCHEMA_VERSIONstays at the version it can handle. Until a migration script AND a helper bump land together, post-migration validation correctly rejects the migrated files and rolls back. This is intentional safety — you can't migrate to a version the helper doesn't yet support.The test suite proves this:
test_run_migration_real_applies_and_validatesruns a v1→v2 migration with the helper at v1, observes the rollback fires, and verifies the original files are restored intact.What's in this PR
New module:
atomic_agents/migrate.py(~600 LOC)MigrationScriptProtocol — defines the script interface--to vN,--dry-run,--status,--rollback,--list-snapshotsTests:
tests/test_migrate.py— 32 tests, all passingvprefix optional)Total suite: 190/190 passing.
Spec coverage
Per spec/03 acceptance criteria (issue #4):
<agents_root>/_migrations/applies_to()+migrate(path, dry_run)_schema.pyfrom v0.1)<agents_root>)--to,--dry-run,--status,--rollback,--list-snapshots_schemato expose old-schema read adaptation per spec/03 — that's a follow-up to wire intoagent.py's capture write path.Test plan
uv run pytest tests/test_migrate.py— 32 tests passuv run pytest— full suite (190 tests) passesv1_to_v2.pyscript and run it against a vault with real data🤖 Generated with Claude Code