miroir/notes/miroir-r3j.4.md
jedarden 30fe7895e4 miroir-r3j.4: Verify P3.4 schema versioning implementation
The schema versioning system is already fully implemented.
Verified all acceptance criteria:

- First run creates schema at initial version (SQLite: schema_versions table)
- Second run is no-op (pending_migrations returns empty)
- Store version > binary version fails with SchemaVersionAhead error
- Both SQLite and Redis share migration metadata via build_registry()

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 07:35:14 -04:00

4.3 KiB

P3.4 Schema Versioning Verification

Summary

The schema versioning system described in the bead acceptance criteria is already fully implemented in the codebase. This document verifies that all acceptance criteria are met.

Implementation Components

1. Schema Migrations Module (crates/miroir-core/src/schema_migrations.rs)

  • Migration struct: Holds version number and SQL content
  • MigrationRegistry: Manages migration lifecycle
    • max_version(): Returns highest migration version (binary version)
    • pending_migrations(current_version): Returns migrations to apply
    • validate_version(store_version): Checks store ≤ binary version
  • build_registry(): Embeds migration files via include_str! macro

2. SQLite Backend (crates/miroir-core/src/task_store/sqlite.rs)

Migration table: schema_versions (version INTEGER PRIMARY KEY, applied_at INTEGER)

run_migration() function:

  1. Creates schema_versions table if not exists
  2. Reads MAX(version) as current store version (0 if empty)
  3. Validates store_version ≤ binary_version via registry().validate_version()
  4. Applies pending migrations in order
  5. Records each migration in schema_versions

3. Redis Backend (crates/miroir-core/src/task_store/redis.rs)

Migration key: miroir:schema_version (holds single integer)

migrate() method:

  1. Gets current value from miroir:schema_version key (None if new)
  2. Validates store_version ≤ binary_version
  3. Sets miroir:schema_version = binary_version
  4. Note: Redis doesn't need SQL migrations (no tables), but tracks version for compatibility

4. Startup Integration (crates/miroir-core/src/task_registry.rs)

Both TaskRegistry::sqlite() and TaskRegistry::redis() call store.migrate() immediately after opening the store.

Acceptance Criteria Verification

Criterion Status Evidence
First run creates schema at initial version SQLite: run_migration() creates schema_versions table, applies migrations 001-003
Second run is no-op; single SELECT Test migration_is_idempotent passes; pending_migrations() returns empty when current == max
Store version > binary version fails Test schema_version_ahead_fails validates SchemaVersionAhead error
Both backends share migration metadata Both use schema_migrations::build_registry() and MigrationRegistry

Migration Files

  • 001_initial.sql: Core tables (tasks, node_settings_version, aliases, sessions, idempotency_cache, jobs, leader_lease)
  • 002_feature_tables.sql: Feature tables (canaries, canary_runs, cdc_cursors, tenant_map, rollover_policies, search_ui_config, admin_sessions)
  • 003_task_registry_fields.sql: No-op (fields already in 001)

Test Coverage

schema_migrations module tests (all pass):

  • test_registry_max_version
  • test_pending_migrations
  • test_validate_version_success
  • test_validate_version_store_ahead
  • test_duplicate_version_panics

SQLite backend tests (all pass):

  • migration_is_idempotent: Verifies second migrate() call is no-op
  • schema_version_recorded: Verifies MAX(version) matches binary version
  • schema_version_ahead_fails: Verifies error when store_version > binary_version
  • task_survives_store_reopen: Verifies data survives close/reopen cycle
  • all_tables_survive_store_reopen: Verifies all 14 tables persist

Redis backend test (code correct, requires Docker):

  • test_redis_migrate: Verifies migrate() succeeds (requires testcontainers Redis)

Error Handling

MiroirError::SchemaVersionAhead:

SchemaVersionAhead {
    store_version: i64,  // The store's migration version
    binary_version: i64, // This binary's max migration version
}

This error is returned when:

  • SQLite: schema_versions.MAX(version) > binary_version
  • Redis: miroir:schema_version > binary_version

Version Numbering

  • Monotonic integers: 1, 2, 3, ... (not 001, 002 as in filenames)
  • Version 1 = 001_initial.sql
  • Version 2 = 002_feature_tables.sql
  • Version 3 = 003_task_registry_fields.sql
  • Binary version = registry().max_version()

Conclusion

The P3.4 schema versioning system is complete and operational. All acceptance criteria are met, tests pass, and both SQLite and Redis backends share the same migration metadata structure via schema_migrations::build_registry().