بنقرة واحدة
db-migration
Safe pattern for adding SQLite schema migrations in the deep-cuts Rust/rusqlite_migration stack
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Safe pattern for adding SQLite schema migrations in the deep-cuts Rust/rusqlite_migration stack
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Experimental protocol for Deep Cuts research, prototypes, model evaluations, threshold tuning, ablations, metric comparisons, and claims about accuracy or quality. Use before running or interpreting experiments so bots preserve train/validation/test boundaries, avoid leakage, compare against baselines, and report results honestly.
Guidelines for creating, updating, reorganizing, and reviewing Deep Cuts documentation in the project wiki, including page taxonomy, lifecycle status, protected pages, proposal handling, and link verification.
Pattern for multi-agent collaboration sessions in the deep-cuts fam — forge-first coordination over the botfam substrate (Gitea issues/PRs as the coordination plane, the unified `botfam wait` wake loop, bare-actor worktrees), with IRC opt-in for design sprints, plus session-log conventions
Draft, structure, and finalize a Deep Cuts blog-series post so it conforms to the post template, house voice, and cross-post link rules. Use when writing, drafting, assembling, or editing a "Deep Cuts" blog post (the rlupi.com series), preparing a post for publishing, or adding front matter to one. Covers the template, front matter schema, voice, agent crediting, and the link linter.
Checklist and guide for adding a new analysis pass to the trait-based modular pipeline
How to run Python scripts and install packages in the deep-cuts project
| name | db-migration |
| description | Safe pattern for adding SQLite schema migrations in the deep-cuts Rust/rusqlite_migration stack |
Schema changes are managed by rusqlite_migration. The crate applies migrations in order and tracks the applied version in an internal __migrations table. Once a migration is shipped it is immutable — the only safe path forward is a new migration.
get_migrations() in src-tauri/src/database.rs.src-tauri/migrations/ as zero-padded, index-prefixed .sql files (e.g. 07_my_new_column.sql) and be loaded via include_str!().Track, WatchedDirectory) rather than written inlined inside Tauri command handlers.Create the SQL file in src-tauri/migrations/ using zero-padded prefixes. For example, src-tauri/migrations/07_my_new_column.sql:
ALTER TABLE tracks ADD COLUMN <new_column> TEXT;
Register it at the end of the get_migrations() array in src-tauri/src/database.rs:
M::up(include_str!("../migrations/07_my_new_column.sql")),
For a new table, create src-tauri/migrations/08_my_new_table.sql:
CREATE TABLE IF NOT EXISTS my_new_table (
id INTEGER PRIMARY KEY AUTOINCREMENT,
track_id INTEGER NOT NULL,
value REAL,
FOREIGN KEY(track_id) REFERENCES tracks(id) ON DELETE CASCADE
);
CREATE INDEX idx_my_new_table_track ON my_new_table(track_id);
Register it in database.rs:
M::up(include_str!("../migrations/08_my_new_table.sql")),
Tauri commands should remain extremely thin and decoupled from raw SQL queries.
lib.rs handlers.src-tauri/src/database.rs:impl Track {
pub fn find_all(conn: &Connection) -> Result<Vec<Self>, rusqlite::Error> {
// Build the SELECT from the canonical column list; map rows by name.
let sql = format!(
"SELECT {} FROM tracks ORDER BY artist ASC",
Self::COLUMN_LIST.join(", "),
);
let mut stmt = conn.prepare(&sql)?;
stmt.query_map([], Self::from_row)?.collect()
}
}
Track::COLUMN_LIST and Track::from_row are generated by the
db_row_mapping!(Track { ... }) macro in database.rs from a single field
list. Rows are mapped by column name (row.get("title")), so the SELECT
order is irrelevant and there is no positional row.get(N) to keep aligned.
New SELECT * FROM tracks-style queries should reuse Track::COLUMN_LIST
rather than hand-writing the column list again.
#[tauri::command]
fn get_tracks(conn_state: tauri::State<'_, Mutex<Connection>>) -> Result<Vec<Track>, String> {
let conn = conn_state.lock().map_err(|e| e.to_string())?;
Track::find_all(&conn).map_err(|e| e.to_string())
}
Update Rust structs — if the new column is read anywhere (e.g. the Track struct in database.rs), add the field with Option<T> to handle pre-migration rows. For Track, also add the column name to the db_row_mapping!(Track { ... }) list directly below the struct — the field name must equal the column name. The compiler enforces that the struct and the macro list stay in sync: a name in only one place fails to build, so there is no silent drift. (You do not need to touch find_all/find — they read from COLUMN_LIST automatically.)
Update sidecar structs — if the new column holds ML-derived data that should survive a library rescan, wire it into src-tauri/src/scanner/sidecar.rs in three places:
SidecarMlMetadata (with Option<T>)SELECT in save() and assign it in the struct literalSET <column> = ? to the UPDATE statement in restore()Run the test suite — the in-memory test DB exercises every migration on each run:
cargo test --manifest-path src-tauri/Cargo.toml
A failing migration test means your SQL is malformed or conflicts with an earlier migration. The database::tests migration-invariant tests also assert that expected tables, indexes, virtual tables, and every Track-mapped column exist after all migrations — so a dropped or renamed mapped column fails here rather than at runtime.
Verify from scratch — wipe the dev DB to confirm all migrations apply cleanly:
npm run tauri dev
(or delete ~/Library/Application Support/com.rlupi.deep-cuts/deep_cuts.db first)
If adding an embedding table, use vec0 syntax:
M::up("
CREATE VIRTUAL TABLE IF NOT EXISTS my_embeddings USING vec0(
track_id INTEGER PRIMARY KEY,
embedding float[<dim>]
);
"),
The sqlite-vec extension is loaded at app startup via DbManager. Do not remove or reorder those load calls.
~/Library/Application Support/com.rlupi.deep-cuts/deep_cuts.db
Back it up before testing destructive migrations manually.