diff --git a/content/docs/getting-started/cli.mdx b/content/docs/getting-started/cli.mdx index ac06445c91..23cdaae6b8 100644 --- a/content/docs/getting-started/cli.mdx +++ b/content/docs/getting-started/cli.mdx @@ -419,6 +419,49 @@ os info --json # JSON output for tooling Loaded in 90ms ``` +### Schema migrations + +The metadata→database sync is **additive-only**: on boot it creates missing +tables and adds new columns, but never alters or drops existing ones. So a +*non-additive* change to an object already backed by a database — relaxing +`required` (drop `NOT NULL`), changing a field's type/length, or removing a +field — silently diverges from the live schema, and the physical column wins at +write time. `os migrate` reconciles the database to the metadata (the source of +truth). + +| Command | Description | +|---------|-------------| +| `os migrate plan` | Dry-run: show how the database has drifted from metadata, categorised safe / needs-confirm / destructive (no changes applied) | +| `os migrate apply` | Reconcile the database to metadata. Applies loosening changes; destructive ones require `--allow-destructive` | + +```bash +os migrate plan # Preview drift (no changes) +os migrate apply # Apply safe (loosening) changes, with a confirm prompt +os migrate apply --yes # Skip the prompt (CI / scripts) +os migrate apply --allow-destructive --yes # Also drop orphaned columns, tighten NOT NULL, narrow types +os migrate plan --json # Machine-readable output +``` + +| Category | Examples | Applied by | +|----------|----------|------------| +| `safe` | relax `NOT NULL` → nullable, widen a `varchar` | `os migrate apply` (and dev auto-reconcile) | +| `needs_confirm` | non-narrowing type change | `os migrate apply` | +| `destructive` | drop an orphaned column, tighten `NOT NULL`, narrow a type | `os migrate apply --allow-destructive` | + + +**Dev self-heal.** `os dev` runs the SQL driver with `autoMigrate: 'safe'`, so +loosening changes (e.g. you just made a field optional) are applied to your +existing dev database automatically on restart — no `os migrate` needed, no data +loss. Auto-reconcile is **dev-only and never destructive**; it is force-disabled +under `NODE_ENV=production`, where you run `os migrate` deliberately. + + + +`os migrate` only sees objects in your **compiled artifact** — run `os build` +first. It never drops a table that is absent from your metadata, and on SQLite +it reconciles via a table rebuild (copy → swap) that preserves your data. + + ### Scaffolding | Command | Alias | Description | diff --git a/skills/objectstack-data/SKILL.md b/skills/objectstack-data/SKILL.md index 0326fc44c2..4d0623a4fe 100644 --- a/skills/objectstack-data/SKILL.md +++ b/skills/objectstack-data/SKILL.md @@ -260,6 +260,27 @@ export default ObjectSchema.create({ --- +## Schema evolution on an existing database + +The metadata→DB sync is **additive-only**: new tables/columns are created on +boot, but existing columns are **never** altered or dropped. A non-additive +change to an object that already has data silently diverges from the physical +schema, and the **database column wins at write time** (#2186): + +| Change | Existing DB on restart | +|--------|------------------------| +| add object / field / index | ✅ applied automatically (additive) | +| `required: true → false` (relax `NOT NULL`) | dev auto-heals (`autoMigrate:'safe'`); otherwise `os migrate apply` | +| type / length change, drop field, rename | `os migrate apply` (`--allow-destructive` for drops / tightenings) | + +Tell-tale: `/meta` reports a field optional but a write still 400s +`" is required"` — that is a stale `NOT NULL` column (physical drift), +**not** a validator bug. `os dev` reconciles loosening automatically; otherwise +`os migrate plan` to preview and `os migrate apply` to reconcile. CLI details: +see **objectstack-platform**. + +--- + ## Common Patterns ### Naming Rules Summary diff --git a/skills/objectstack-platform/SKILL.md b/skills/objectstack-platform/SKILL.md index 0e0256f09c..d63546741a 100644 --- a/skills/objectstack-platform/SKILL.md +++ b/skills/objectstack-platform/SKILL.md @@ -563,6 +563,8 @@ cd my-app && pnpm install os dev --ui # dev server + Studio (auto-hops port if taken) os validate # metadata cross-reference checks os compile # produce dist/ artifact +os migrate plan # preview metadata↔DB schema drift (additive sync never alters existing columns) +os migrate apply # reconcile DB to metadata (loosening only; --allow-destructive for drops/tightenings) PORT=8080 os start # production — pin the port explicitly (see Ports & networking) ```