db-migration
Safe pattern for adding SQLite schema migrations in the deep-cuts Rust/rusqlite_migration stack
Skill by robertolupi
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/robertolupi-db-migration
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/db-migration
What's inside
- Skill name
db-migration- Description
- Safe pattern for adding SQLite schema migrations in the deep-cuts Rust/rusqlite_migration stack
- License
- AGPL-3.0
- Instructions
- 138 lines in SKILL.md · view on GitHub
Files (1)
- SKILL.md
SKILL.md (first lines)
# Adding a DB Schema Migration
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.
---
## Rules
- **Never edit an existing migration.** Changing past migrations will corrupt databases that have already applied them.
- **Never reorder migrations.** Index position is the version number.
- **Always add at the end** of the array returned by `get_migrations()` in `src-tauri/src/database.rs`.
- **Use External SQL Files**: All migration SQL scripts must reside in `src-tauri/migrations/` as zero-padded, index-prefixed `.sql` files (e.g. `07_my_new_column.sql`) and be loaded via `include_str!()`.
- **Avoid Inline SQL in Handlers**: Database interactions (queries, updates) should be encapsulated inside repository methods on the database structs (`Track`, `WatchedDirectory`) rather than written inlined inside Tauri command handlers.
---
## Adding a migration
1. **Create the SQL file** in `src-tauri/migrations/` using zero-padded prefixes. For example, `src-tauri/migrations/07_my_new_column.sql`:
```sql
ALTER TABLE tracks ADD COLUMN <new_column> TEXT;
```
2. **Register it at the end** of the `get_migrations()` array in `src-tauri/src/database.rs`:
```rust
M::up(include_str!("../migrations/07_my_new_column.sql")),
```
For a new table, create `src-tauri/migrations/08_my_new_table.sql`:
```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`:
```rust
M::up(include_str!("../migrations/08_my_new_table.sql")),
```
---
## Encapsulating Database Code (Repository Pattern)
Tauri commands should remain extremely thin and decoupled from raw SQL queries.
* **Do not** execute prepare statements or map query rows inside `lib.rs` handlers.
* **Instead**, implement clean CRUD repository methods directly on the domain structs in `src-tauri/src/database.rs`:
```rust
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.
* Call them from your Tauri command:
```rust
#[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())
}
```
---
## After adding the migration
1. **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.)
2. **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:
- Add the field to `SidecarMlMetadata` (with `Option<T>`)
- Add the column to the `SELECT` in `save()` and assign it in the struct literal
- Add a `SET <column> = ?` to the `UPDATE` statement in `restore()`
3. **Run the test suite** — the in-memory test DB exercises every migration on each run:
```bash
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.
3. **Verify from scratch** — wipe the dev DB to confirm all migrations apply cleanly:
```bash
npm run tauri dev
```
(or delete `~/Library/Application Support/com.rlupi.deep-cuts/deep_cuts.db` first)
---
## sqlite-vec virtual tables
If adding an embedding table, use `vec0` syntax:
```rust
M::up("
CREATE VIRTUAL TABLE IF NOT EXISTS my_embeddings USING vec0(From robertolupi/deep-cuts, AGPL-3.0.
More from robertolupi/deep-cuts
- 0.534score
how-to-experiment
robertolupi
Experimental protocol for Deep Cuts research, prototypes, model evaluations, threshold tuning, ablations, metric comparisons, and claims about accuracy or…
SkillCFreshness: C★ 7Claude Code +9 - 0.534score
ui-debug
robertolupi
Inspect, debug, and compare the Deep Cuts UI using an available browser tool.
SkillCFreshness: C★ 7Claude Code +9 - 0.534score
write-blog-post
robertolupi
Draft, structure, and finalize a Deep Cuts blog-series post so it conforms to the post template, house voice, and cross-post link rules.
SkillCFreshness: C★ 7Claude Code +9 - 0.531score
dev-guidelines
robertolupi
Guidelines to prevent development false starts, environment mismatches, and incorrect commands in the deep-cuts monorepo
SkillCFreshness: C★ 7Claude Code +9 - 0.531score
git-commits
robertolupi
Rules for writing commit messages in the deep-cuts repository — subject line format, body content, scope naming, and what to omit
SkillCFreshness: C★ 7Claude Code +9 - 0.531score
ui-design
robertolupi
Guidelines for the Sonic Glitch design system, theme variables, and multi-theme readability (dark, light, high-contrast)
SkillCFreshness: C★ 7Claude Code +9 - 0.531score
add-tauri-sidecar
robertolupi
How to bundle an external binary and its dylib dependencies with the Tauri app, patch rpaths, sign everything, and resolve the path at runtime
SkillCFreshness: C★ 7Claude Code +9 - 0.531score
release-build
robertolupi
End-to-end checklist for building a signed macOS release — bump manifest min_app_version, verify sidecars, build, and inspect the .app
SkillCFreshness: C★ 7Claude Code +9 - 0.528score
add-analysis-pass
robertolupi
Checklist and guide for adding a new analysis pass to the trait-based modular pipeline
SkillCFreshness: C★ 7Claude Code +9 - 0.528score
add-ipc-command
robertolupi
Pattern for adding a new Tauri IPC command (request/response or push event) in the deep-cuts monorepo
SkillCFreshness: C★ 7Claude Code +9 - 0.528score
bump-dev-version
robertolupi
Bump the app version in Cargo.toml after a release to start the next dev cycle
SkillCFreshness: C★ 7Claude Code +9 - 0.528score
query-db
robertolupi
How to locate and query the deep-cuts production SQLite database
SkillCFreshness: C★ 7Claude Code +9
Signals
- Stars
- 7
- Forks
- 1
- Open issues
- 0
- Last commit (this folder)
- Jun 8, 2026 (4 months ago)
- Last release
- Jun 8, 2026 (4 months ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- AGPL-3.0
- Repository
- github.com/robertolupi/deep-cuts
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from robertolupi
- 0.534score
write-blog-post
robertolupi
Draft, structure, and finalize a Deep Cuts blog-series post so it conforms to the post template, house voice, and cross-post link rules.
SkillCFreshness: C★ 7Claude Code +9 - 0.534score
how-to-experiment
robertolupi
Experimental protocol for Deep Cuts research, prototypes, model evaluations, threshold tuning, ablations, metric comparisons, and claims about accuracy or…
SkillCFreshness: C★ 7Claude Code +9 - 0.534score
ui-debug
robertolupi
Inspect, debug, and compare the Deep Cuts UI using an available browser tool.
SkillCFreshness: C★ 7Claude Code +9 - 0.531score
add-tauri-sidecar
robertolupi
How to bundle an external binary and its dylib dependencies with the Tauri app, patch rpaths, sign everything, and resolve the path at runtime
SkillCFreshness: C★ 7Claude Code +9 - 0.531score
dev-guidelines
robertolupi
Guidelines to prevent development false starts, environment mismatches, and incorrect commands in the deep-cuts monorepo
SkillCFreshness: C★ 7Claude Code +9 - 0.531score
git-commits
robertolupi
Rules for writing commit messages in the deep-cuts repository — subject line format, body content, scope naming, and what to omit
SkillCFreshness: C★ 7Claude Code +9
Cite this page
db-migration skill. Agent Mart. https://agentmart.dev/skills/robertolupi-db-migration (last updated 2026-10-24)