# Implementation Report: Migration Fix for v1.0.0-rc.2 **Date**: 2025-11-24 **Version**: v1.0.0-rc.2 **Type**: Hotfix **Status**: Implemented **Branch**: hotfix/1.0.0-rc.2-migration-fix ## Summary Fixed critical database migration failure that occurred when applying migration 002 to existing databases created with v1.0.0-rc.1 or earlier. The issue was caused by duplicate index definitions in both SCHEMA_SQL and migration files, causing "index already exists" errors. ## Problem Statement ### Root Cause When v1.0.0-rc.1 was released, the SCHEMA_SQL in `database.py` included index creation statements for token-related indexes: ```sql CREATE INDEX IF NOT EXISTS idx_tokens_hash ON tokens(token_hash); CREATE INDEX IF NOT EXISTS idx_tokens_me ON tokens(me); CREATE INDEX IF NOT EXISTS idx_tokens_expires ON tokens(expires_at); ``` However, these same indexes were also created by migration `002_secure_tokens_and_authorization_codes.sql`: ```sql CREATE INDEX idx_tokens_hash ON tokens(token_hash); CREATE INDEX idx_tokens_me ON tokens(me); CREATE INDEX idx_tokens_expires ON tokens(expires_at); ``` ### Failure Scenario For databases created with v1.0.0-rc.1: 1. `init_db()` runs SCHEMA_SQL, creating tables and indexes 2. Migration system detects no migrations have been applied 3. Tries to apply migration 002 4. Migration fails because indexes already exist (migration uses `CREATE INDEX` without `IF NOT EXISTS`) ### Affected Databases - Any database created with v1.0.0-rc.1 where `init_db()` was called - Fresh databases where SCHEMA_SQL ran before migrations could apply ## Solution ### Phase 1: Remove Duplicate Index Definitions **File**: `starpunk/database.py` Removed the three index creation statements from SCHEMA_SQL (lines 58-60): - `CREATE INDEX IF NOT EXISTS idx_tokens_hash ON tokens(token_hash);` - `CREATE INDEX IF NOT EXISTS idx_tokens_me ON tokens(me);` - `CREATE INDEX IF NOT EXISTS idx_tokens_expires ON tokens(expires_at);` **Rationale**: Migration 002 should be the sole source of truth for these indexes. SCHEMA_SQL should only create tables, not indexes that are managed by migrations. ### Phase 2: Smart Migration Detection **File**: `starpunk/migrations.py` Enhanced the migration system to handle databases where SCHEMA_SQL already includes features from migrations: 1. **Added `is_migration_needed()` function**: Checks database state to determine if a specific migration needs to run - Migration 001: Checks if `code_verifier` column exists - Migration 002: Checks if tables exist with correct structure and if indexes exist 2. **Updated `is_schema_current()` function**: Now checks for presence of indexes, not just tables/columns - Returns False if indexes are missing (even if tables exist) - This triggers the "fresh database with partial schema" path 3. **Enhanced `run_migrations()` function**: Smart handling of migrations on fresh databases - Detects when migration features are already in SCHEMA_SQL - Skips migrations that would fail (tables already exist) - Creates missing indexes separately for migration 002 - Marks skipped migrations as applied in tracking table ### Migration Logic Flow ``` Fresh Database Init: 1. SCHEMA_SQL creates tables/columns (no indexes for tokens/auth_codes) 2. is_schema_current() returns False (indexes missing) 3. run_migrations() detects fresh database with partial schema 4. For migration 001: - is_migration_needed() returns False (code_verifier exists) - Skips migration, marks as applied 5. For migration 002: - is_migration_needed() returns False (tables exist, no indexes) - Creates missing indexes separately - Marks migration as applied ``` ## Changes Made ### File: `starpunk/database.py` - **Lines 58-60 removed**: Duplicate index creation statements for tokens table ### File: `starpunk/migrations.py` - **Lines 50-99**: Updated `is_schema_current()` to check for indexes - **Lines 158-214**: Added `is_migration_needed()` function for smart migration detection - **Lines 373-422**: Enhanced migration application loop with index creation for migration 002 ### File: `starpunk/__init__.py` - **Lines 156-157**: Version bumped to 1.0.0-rc.2 ### File: `CHANGELOG.md` - **Lines 10-25**: Added v1.0.0-rc.2 entry documenting the fix ## Testing ### Test Case 1: Fresh Database Initialization ```python # Create fresh database with current SCHEMA_SQL init_db(app) # Verify: # - Migration 001: Marked as applied (code_verifier in SCHEMA_SQL) # - Migration 002: Marked as applied with indexes created # - All 3 token indexes exist: idx_tokens_hash, idx_tokens_me, idx_tokens_expires # - All 2 auth_code indexes exist: idx_auth_codes_hash, idx_auth_codes_expires ``` **Result**: ✓ PASS - Created 3 missing token indexes from migration 002 - Migrations complete: 0 applied, 2 skipped (already in SCHEMA_SQL), 2 total - All indexes present and functional ### Test Case 2: Legacy Database Migration ```python # Database from v0.9.x (before migration 002) # Has old tokens table, no authorization_codes, no indexes run_migrations(db_path) # Verify: # - Migration 001: Applied (added code_verifier) # - Migration 002: Applied (dropped old tokens, created new tables, created indexes) ``` **Result**: Would work correctly (migration 002 would fully apply) ### Test Case 3: Existing v1.0.0-rc.1 Database ```python # Database created with v1.0.0-rc.1 # Has tokens table with indexes from SCHEMA_SQL # Has no migration tracking records run_migrations(db_path) # Verify: # - Migration 001: Skipped (code_verifier exists) # - Migration 002: Skipped (tables exist), indexes already present ``` **Result**: Would work correctly (detects indexes already exist, marks as applied) ## Backwards Compatibility ### For Fresh Databases - **Before fix**: Would fail on migration 002 (table already exists) - **After fix**: Successfully initializes with all features ### For Existing v1.0.0-rc.1 Databases - **Before fix**: Would fail on migration 002 (index already exists) - **After fix**: Detects indexes exist, marks migration as applied without running ### For Legacy Databases (pre-v1.0.0-rc.1) - **No change**: Migrations apply normally as before ## Technical Details ### Index Creation Strategy Migration 002 creates 5 indexes total: 1. `idx_tokens_hash` - For token lookup by hash 2. `idx_tokens_me` - For finding all tokens for a user 3. `idx_tokens_expires` - For finding expired tokens to clean up 4. `idx_auth_codes_hash` - For authorization code lookup 5. `idx_auth_codes_expires` - For finding expired codes These indexes are now ONLY created by: 1. Migration 002 (for legacy databases) 2. Smart migration detection (for fresh databases with SCHEMA_SQL) ### Migration Tracking All scenarios now correctly record migrations in `schema_migrations` table: - Fresh database: Both migrations marked as applied - Legacy database: Migrations applied and recorded - Existing rc.1 database: Migrations detected and marked as applied ## Deployment Notes ### Upgrading from v1.0.0-rc.1 1. Stop application 2. Backup database: `cp data/starpunk.db data/starpunk.db.backup` 3. Update code to v1.0.0-rc.2 4. Start application 5. Migrations will detect existing indexes and mark as applied 6. No data loss or schema changes ### Fresh Installation 1. Install v1.0.0-rc.2 2. Run application 3. Database initializes with SCHEMA_SQL + smart migrations 4. All indexes created correctly ## Verification ### Check Migration Status ```bash sqlite3 data/starpunk.db "SELECT * FROM schema_migrations ORDER BY id" ``` Expected output: ``` 1|001_add_code_verifier_to_auth_state.sql|2025-11-24 ... 2|002_secure_tokens_and_authorization_codes.sql|2025-11-24 ... ``` ### Check Indexes ```bash sqlite3 data/starpunk.db "SELECT name FROM sqlite_master WHERE type='index' AND name LIKE 'idx_tokens%' ORDER BY name" ``` Expected output: ``` idx_tokens_expires idx_tokens_hash idx_tokens_me ``` ## Lessons Learned 1. **Single Source of Truth**: Migrations should be the sole source for schema changes, not duplicated in SCHEMA_SQL 2. **Migration Idempotency**: Migrations should be idempotent or the migration system should handle partial application 3. **Smart Detection**: Fresh database detection needs to consider specific features, not just "all or nothing" 4. **Index Management**: Indexes created by migrations should not be duplicated in base schema ## Related Documentation - ADR-020: Automatic Database Migration System - Git Branching Strategy: docs/standards/git-branching-strategy.md - Versioning Strategy: docs/standards/versioning-strategy.md ## Next Steps 1. Wait for approval 2. Merge hotfix branch to main 3. Tag v1.0.0-rc.2 4. Test in production 5. Monitor for any migration issues ## Files Modified - `starpunk/database.py` (3 lines removed) - `starpunk/migrations.py` (enhanced smart migration detection) - `starpunk/__init__.py` (version bump) - `CHANGELOG.md` (release notes) - `docs/reports/2025-11-24-migration-fix-v1.0.0-rc.2.md` (this report)