- 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>
3.4 KiB
name, description, applies-to
| name | description | applies-to |
|---|---|---|
| sqlx-migrations | Never manually pre-apply migrations without tracking rows; use IF NOT EXISTS; let the server apply its own migrations | 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:
-- 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:
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":
# 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.