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>
This commit is contained in:
jedarden 2026-05-20 07:35:14 -04:00
parent d8d81a12a8
commit 30fe7895e4

101
notes/miroir-r3j.4.md Normal file
View file

@ -0,0 +1,101 @@
# 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`**:
```rust
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()`.