Skip to main content

schema-migration

Add or modify a PostgreSQL schema migration for the CMS or Facts store. Covers creating migration functions, stored procedure changes, diff file generation, and TypeScript caller updates.

Source facts

Repository
microsoft/PilotSwarm
Last source activity
April 17, 2026 at 21:59
Detected SKILL.md language
English
Stars
14
Forks
7

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
schema-migration
description
Add or modify a PostgreSQL schema migration for the CMS or Facts store. Covers creating migration functions, stored procedure changes, diff file generation, and TypeScript caller updates.
# Schema Migration The CMS and Facts stores use versioned SQL migrations with advisory-lock serialization. All Postgres data access goes through stored procedures created by these migrations. Schema changes follow the same pattern as duroxide-pg: ordered migration functions, idempotent DDL, and a required companion diff file for code review. ## Architecture ``` pg-migrator.ts — shared advisory-lock migration runner cms-migrator.ts — thin CMS wrapper (lock seed 0x636D73) facts-migrator.ts — thin Facts wrapper (lock seed 0x666163) cms-migrations.ts — ordered CMS migration list facts-migrations.ts — ordered Facts migration list ``` Each system maintains its own `schema_migrations` table within its Postgres schema (`copilot_sessions` for CMS, `pilotswarm_facts` for Facts). Migrations are applied automatically on `initialize()`. ## Adding a New Migration ### 1. Write the migration function Add a new entry to `CMS_MIGRATIONS()` in `cms-migrations.ts` or `FACTS_MIGRATIONS()` in `facts-migrations.ts`: ```typescript { version: "NNNN", name: "descriptive_name", sql: migration_NNNN_descriptive_name(schema), }, ``` Then define the function: ```typescript function migration_NNNN_descriptive_name(schema: string): string { const s = `"${schema}"`; return ` -- NNNN_descriptive_name: what this migration does. -- DDL changes (idempotent) CREATE TABLE IF NOT EXISTS ${s}.new_table (...); ALTER TABLE ${s}.existing_table ADD COLUMN IF NOT EXISTS new_col TEXT; -- Stored procedure changes CREATE OR REPLACE FUNCTION ${s}.my_proc(...) RETURNS ... AS $$ BEGIN ... END; $$ LANGUAGE plpgsql; `; } ``` ### 2. Key rules for migration SQL - **Idempotent**: Use `IF NOT EXISTS`, `IF EXISTS`, `CREATE OR REPLACE` so re-running is safe. - **Schema-parameterized**: The `schema` argument is interpolated into all qualified names. Never hard-code a schema name. - **Stored procedures**: All new data-access queries must be stored procedures (`CREATE OR REPLACE FUNCTION`). No inline SQL in TypeScript. - **Transactional**: Each migration runs inside its own `BEGIN`/`COMMIT` block (handled by the migrator). - **Version numbering**: Use sequential 4-digit zero-padded versions (`0001`, `0002`, ...). Check the current highest version in the migrations list. ### 3. Generate the diff file (REQUIRED) Every migration that modifies schema or stored procedures **must** have a companion diff markdown file. This is required because git diffs for SQL-in-TypeScript migrations only show the new code, not the delta from the previous version. Create the diff file at `packages/sdk/src/migrations/NNNN_diff.md` (CMS) or `packages/sdk/src/migrations/NNNN_facts_diff.md` (Facts). #### Diff format ```markdown # Diff for migration NNNN Migration file: `cms-migrations.ts` — `migration_NNNN_name` ## Table Changes ### `table_name` — new table (full DDL in a ```sql block) ### `table_name` — modified (mark new columns with `+` in a ```diff block) ## New Indexes (full DDL in ```sql blocks, or "None.") ## Function Changes ### `func_name` — new (signature in a ```diff block with `+` markers, prose description) ### `func_name` — body modified (baseline: NNNN) (unified diff in a ```diff block showing changed lines with +/- markers) ``` #### How to produce diffs for modified stored procedures 1. **Find the baseline**: Identify the most recent migration that contains `CREATE OR REPLACE FUNCTION ... func_name`. That is the baseline version. 2. **Extract both bodies**: Copy the function body from the baseline migration and the new migration. 3. **Normalize**: Replace schema placeholders with `SCHEMA.` for consistent diffing. 4. **Diff**: Run `diff -u baseline.sql new.sql` to get unified diff output. 5. **Include**: Place the diff hunks in a ` ```diff ` code block under a heading that notes the baseline migration number. See existing examples: - `packages/sdk/src/migrations/0004_diff.md` (all-new CMS stored procedures) - `packages/sdk/src/migrations/0002_facts_diff.md` (all-new Facts stored procedures) ### 4. Update TypeScript callers After adding or modifying a stored procedure: 1. **Update `sqlForSchema()`** — add the new function name to the `fn` map in `cms.ts` or `facts-store.ts`: ```typescript fn: { myNewProc: `${s}.cms_my_new_proc`, } ``` 2. **Update the provider method** — call the stored proc instead of inline SQL: ```typescript async myMethod(args: MyArgs): Promise<MyResult> { const { rows } = await this.pool.query( `SELECT * FROM ${this.sql.fn.myNewProc}($1, $2)`, [args.foo, args.bar], ); return rows.map(rowToMyResult); } ``` 3. **Row mappers stay in TypeScript** — `rowToSessionRow()`, `rowToSessionEvent()`, etc. still handle PG snake_case → TS camelCase conversion. ### 5. Build and test ```bash cd packages/sdk npm run build # TypeScript compilation npx vitest run test/local/ # integration tests ``` Migrations are applied automatically when `initialize()` is called. Existing deployments will pick up new migrations on next startup. ## Modifying an Existing Stored Procedure Never edit a previous migration. Instead: 1. Add a new migration with `CREATE OR REPLACE FUNCTION` for the modified procedure. 2. Generate a diff file showing the delta from the baseline version. 3. Update the TypeScript caller if the function signature changed. ## Migration System Internals - **Advisory locks**: Each system uses a unique lock seed hashed with the schema name to prevent concurrent workers from double-applying migrations. - **Tracking table**: `{schema}.schema_migrations` with columns `(version TEXT PK, name TEXT, applied_at TIMESTAMPTZ)`. - **Ordering**: Migrations are applied in array order. The version string is compared against the tracking table. - **Error handling**: Failed migrations trigger `ROLLBACK` and re-throw. The migration is not recorded as applied. ## Key files - [packages/sdk/src/pg-migrator.ts](../../../packages/sdk/src/pg-migrator.ts) — shared migration runner - [packages/sdk/src/cms-migrations.ts](../../../packages/sdk/src/cms-migrations.ts) — CMS migration definitions - [packages/sdk/src/facts-migrations.ts](../../../packages/sdk/src/facts-migrations.ts) — Facts migration definitions - [packages/sdk/src/cms-migrator.ts](../../../packages/sdk/src/cms-migrator.ts) — CMS migrator wrapper - [packages/sdk/src/facts-migrator.ts](../../../packages/sdk/src/facts-migrator.ts) — Facts migrator wrapper - [packages/sdk/src/cms.ts](../../../packages/sdk/src/cms.ts) — CMS provider (stored proc callers) - [packages/sdk/src/facts-store.ts](../../../packages/sdk/src/facts-store.ts) — Facts provider (stored proc callers) - [packages/sdk/src/migrations/](../../../packages/sdk/src/migrations/) — diff files for code review
View on GitHub