- Split CODING_GUIDELINES.md into 19 indexed standards files under .claude/standards/ - 9 from CODING_GUIDELINES (conventions, powershell, security, api, git, gururmm) - 10 from session log tribal knowledge (syncro, ssh, gitea, python, client, gururmm) - Add .claude/standards/index.yml for cheap relevance-based lookup - Add /inject-standards command: load targeted standards per task instead of full guidelines - Add /shape-spec command: pre-implementation spec for GuruRMM features (plan.md, shape.md, references.md, standards.md) with mandatory out-of-scope gate - Add docs/tech-stack.md and docs/mission.md for ClaudeTools API - Add projects/msp-tools/guru-rmm/docs/tech-stack.md and mission.md for GuruRMM - Update CLAUDE.md commands table with /inject-standards and /shape-spec Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
78 lines
3.4 KiB
Markdown
78 lines
3.4 KiB
Markdown
---
|
|
name: sqlx-migrations
|
|
description: Never manually pre-apply migrations without tracking rows; use IF NOT EXISTS; let the server apply its own migrations
|
|
applies-to: gururmm
|
|
---
|
|
|
|
# GuruRMM sqlx Migration Discipline
|
|
|
|
## The core rule
|
|
|
|
Never manually pre-apply migrations via psql without also recording the corresponding row in `_sqlx_migrations`. If the row is missing, the server binary will attempt to re-run the migration at startup and fail when it finds the table or column already exists.
|
|
|
|
## The correct workflow
|
|
|
|
Let the server binary apply its own migrations on startup:
|
|
|
|
```
|
|
1. Write the SQL migration file (server/migrations/NNN_description.sql)
|
|
2. Use ADD COLUMN IF NOT EXISTS / CREATE TABLE IF NOT EXISTS for idempotence
|
|
3. Run cargo sqlx prepare (keeps .sqlx/ offline cache current)
|
|
4. Commit the migration file + .sqlx/ changes
|
|
5. Build the server binary (push to Gitea triggers build-server.sh)
|
|
6. Deploy: stop → copy binary → start
|
|
7. sqlx applies the migration on startup and records the checksum row
|
|
```
|
|
|
|
Do not pre-apply the SQL with psql. Do not insert rows into `_sqlx_migrations` manually unless recovering from a specific failure.
|
|
|
|
## Why: the proc macro excludes pre-applied rows
|
|
|
|
When `DATABASE_URL` is set at compile time, `sqlx::migrate!()` queries `_sqlx_migrations` during compilation and embeds only the migrations not yet present in the DB. If you pre-apply migration 026 via psql and its row is in `_sqlx_migrations` before the build, the compiled binary will not contain migration 026 — then at runtime, finding a row for version 26 with no matching embedded migration causes a fatal startup error.
|
|
|
|
The fix: delete the pre-applied `_sqlx_migrations` row(s), rebuild with `SQLX_OFFLINE=true`, let the server apply them naturally.
|
|
|
|
## Write idempotent SQL
|
|
|
|
All migrations use `IF NOT EXISTS` or `IF EXISTS` forms:
|
|
|
|
```sql
|
|
-- Tables
|
|
CREATE TABLE IF NOT EXISTS policy_checks (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
...
|
|
);
|
|
|
|
-- Columns
|
|
ALTER TABLE agents ADD COLUMN IF NOT EXISTS update_channel TEXT
|
|
CHECK (update_channel IN ('stable', 'beta'));
|
|
```
|
|
|
|
This protects against the "table already exists" error if a migration is somehow applied twice, and allows the migration to be run safely during development resets.
|
|
|
|
## SQLX_OFFLINE build environment
|
|
|
|
`SQLX_OFFLINE=true` is set permanently in `/home/guru/.cargo/env` on Saturn. All cargo builds by the `guru` user use the `.sqlx/` offline cache rather than querying the live DB at compile time. This eliminates the proc macro/DB interaction entirely.
|
|
|
|
After any schema change that adds or modifies a `query!()` macro, re-run:
|
|
```bash
|
|
cd /home/guru/gururmm/server && cargo sqlx prepare
|
|
git add server/.sqlx && git commit -m "build: update sqlx offline query cache"
|
|
```
|
|
|
|
## Recovery from _sqlx_migrations mismatch
|
|
|
|
If the server fails to start with "migration N was previously applied but is missing in the resolved migrations":
|
|
|
|
```bash
|
|
# Option 1: Delete the row (if the migration was manually applied and tables exist)
|
|
PGPASSWORD=<pw> psql -h localhost -U gururmm -d gururmm \
|
|
-c "DELETE FROM _sqlx_migrations WHERE version IN (N);"
|
|
# Then rebuild so the binary embeds the migration
|
|
|
|
# Option 2: If checksum mismatch (binary embedded wrong content)
|
|
# Fix the SQL file, rerun cargo sqlx prepare, rebuild, deploy
|
|
```
|
|
|
|
Never delete `_sqlx_migrations` rows for migrations that the current binary does NOT embed — those rows protect against re-running already-applied migrations.
|