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)
```