fix(core): ensure migration framework records schema version automatically

The migration runner now inserts (OR REPLACE) the schema_version row
after each successful migration batch, regardless of whether the
migration SQL itself contains a self-registering INSERT. This prevents
version tracking gaps when a .sql migration omits the bookkeeping
statement, which would leave the schema at an unrecorded version and
cause re-execution attempts on next startup.

Legacy migrations that already self-register are unaffected thanks to
the OR REPLACE conflict resolution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Taylor Eernisse
2026-02-11 10:21:49 -05:00
parent d9f99ef21d
commit 70271c14d6

View File

@@ -143,6 +143,20 @@ pub fn run_migrations(conn: &Connection) -> Result<()> {
match conn.execute_batch(sql) { match conn.execute_batch(sql) {
Ok(()) => { Ok(()) => {
// Framework-managed version bookkeeping: ensures the version is
// always recorded even if a migration .sql omits the INSERT.
// Uses OR REPLACE so legacy migrations that self-register are harmless.
conn.execute(
"INSERT OR REPLACE INTO schema_version (version, applied_at, description) \
VALUES (?1, strftime('%s', 'now') * 1000, ?2)",
rusqlite::params![version, version_str],
)
.map_err(|e| LoreError::MigrationFailed {
version,
message: format!("Failed to record schema version: {e}"),
source: Some(e),
})?;
conn.execute_batch(&format!("RELEASE {}", savepoint_name)) conn.execute_batch(&format!("RELEASE {}", savepoint_name))
.map_err(|e| LoreError::MigrationFailed { .map_err(|e| LoreError::MigrationFailed {
version, version,