diff --git a/.changeset/orphan-search-column-runbook.md b/.changeset/orphan-search-column-runbook.md new file mode 100644 index 000000000..f385ecb64 --- /dev/null +++ b/.changeset/orphan-search-column-runbook.md @@ -0,0 +1,15 @@ +--- +--- + +Document how to handle database-only columns reported by `os migrate plan` after +a platform upgrade (#528): a new "Destructive schema drift" section in +`docs/MAINTENANCE.md` covering how to tell a genuine orphan from a +runtime-provisioned column the planner cannot see, and the safe +`os migrate apply --allow-destructive` procedure (stop the service, back up, +clear every destructive entry against both tests, apply, verify). + +Records the 17.0 `__search` report as a known **false positive**: those 9 columns +are live pinyin search-companion columns, not orphans, and dropping them breaks +search. Root cause is a schema-view mismatch between the migrate CLI and the dev +runtime, filed upstream as objectstack#3955; no cleanup on that class until it is +fixed. Documentation only — releases nothing. diff --git a/docs/MAINTENANCE.md b/docs/MAINTENANCE.md index db8ad62cb..5a2a0f85c 100644 --- a/docs/MAINTENANCE.md +++ b/docs/MAINTENANCE.md @@ -69,6 +69,68 @@ can silently invalidate existing metadata or **seed data** (see §4). Treat ever and restart. This is an environment issue, not an app change. 7. Note the new platform version in `CHANGELOG.md`. +### 3.1 Destructive schema drift — database-only columns after an upgrade + +`os migrate plan` diffs the live database schema against the current metadata. +After a platform upgrade it can report columns that exist in the database but +are not described by any metadata the planner can see. Some are genuine orphans +— companion columns an older platform version provisioned automatically and the +new version no longer does. A genuine orphan is harmless (nothing reads or +writes it), so cleaning it up is **deferred by design**: schedule it for a +maintenance window instead of bundling it into the upgrade itself. Others are +not orphans at all, only invisible to the planner, and dropping them causes an +outage — so the first job is always to tell the two apart. + +> [!WARNING] +> **Not every "orphan" the planner reports is really an orphan.** `os migrate +> plan` describes the schema as the *migrate CLI* understands it, which is not +> always what the *running server* provisioned. A column the runtime creates and +> actively uses can show up as database-only, and dropping it breaks the feature +> that depends on it. Confirm what a column is for before you let anything drop +> it — see the `__search` case below for a live example of this exact trap. + +**Known false positive — `__search` companion columns +([#528](https://github.com/objectstack-ai/hotcrm/issues/528)).** After the 17.0 +upgrade, `os migrate plan` reports 9 database-only `__search` columns +(`crm_competitor`, `crm_opportunity_line_item`, `crm_quote_line_item`, +`crm_task`, `sys_metadata`). **They are not orphans — do not drop them.** +Column-level comparison on two freshly created databases from the same artifact +showed the split clearly: the database created by `objectstack dev` has 34 +columns on `crm_task` *including* `__search`, while the one created by +`os migrate plan --database-url ` has 33 *without* it — and migrate +reports 0 destructive changes against the database it built itself. The +`__search` columns are live pinyin search-companion columns that the dev runtime +provisions and reads; the report is a **schema-view mismatch between the migrate +CLI and the dev runtime**, filed upstream as +[objectstack#3955](https://github.com/objectstack-ai/objectstack/issues/3955). +Dropping them removes working search columns, and the next dev boot may simply +recreate them. **Take no cleanup action on this class until #3955 is fixed.** + +Cleanup procedure — for columns you have *positively identified* as genuine +orphans (the `__search` class above is excluded until #3955 lands): + +1. **Stop the service first.** Applying destructive schema changes under live + traffic is unsafe — see ObjectStack platform issue #526. +2. Back up the database (for a local dev database, copy `.objectstack/data`). +3. Run `os migrate plan` and read the whole plan. Clear **every** destructive + entry against two questions, not one: + - *Does it back a field you still declare?* If yes, stop and fix the metadata + drift instead of dropping the column. + - *Is it a runtime-provisioned companion column* (search/index/derived + helpers such as `__search`)? These belong to the running server, not to + your metadata, so the planner cannot vouch for them. If you cannot prove a + column is dead, treat it as live and stop. + + A useful proof: point `os migrate plan --database-url` at a **freshly + created** database from the same artifact and compare column lists. Anything + present in both is being provisioned on purpose, whatever the plan calls it. +4. Run `os migrate apply --allow-destructive`. +5. Restart the service and smoke-test: `os migrate plan` should now be clean, + and global search in the Console should still return records. + +`--allow-destructive` drops columns irreversibly — never run it without the +backup from step 2, and never against a database whose plan you have not read. + ## 4. Seed-data staleness — the #1 HotCRM pitfall Stale seed data is the most common cause of "Studio shows a red