From fba1f4fa9b858b61f00a01572022d1c4c75eee1f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 13:42:52 +0000 Subject: [PATCH 1/2] docs(maintenance): runbook for orphan __search column cleanup after 17.0 (#528) ObjectStack 17.0 tightened the conditions under which full-text __search companion columns are provisioned, leaving 9 orphan columns in databases that lived through <=16.x (crm_competitor, crm_opportunity_line_item, crm_quote_line_item, crm_task, sys_metadata, among others). The cleanup itself is a runtime operation (os migrate apply --allow-destructive) that must run against each deployed database, so the repo deliverable is the runbook: a new "Destructive schema drift" section in docs/MAINTENANCE.md documenting when os migrate plan reports database-only columns, the known 17.0 __search case, and the safe apply procedure (stop the service per platform issue #526, back up, review the plan, apply, verify). Closes #528 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01UR21JtWgnthiiQKBHwKpJX --- .changeset/orphan-search-column-runbook.md | 9 ++++++ docs/MAINTENANCE.md | 33 ++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 .changeset/orphan-search-column-runbook.md diff --git a/.changeset/orphan-search-column-runbook.md b/.changeset/orphan-search-column-runbook.md new file mode 100644 index 000000000..dcfc0bb07 --- /dev/null +++ b/.changeset/orphan-search-column-runbook.md @@ -0,0 +1,9 @@ +--- +--- + +Document the cleanup runbook for orphan `__search` companion columns left behind +when ObjectStack 17.0 tightened search-column provisioning (#528): a new +"Destructive schema drift" section in `docs/MAINTENANCE.md` covering when +`os migrate plan` reports database-only columns, the known 17.0 `__search` case, +and the safe `os migrate apply --allow-destructive` procedure (stop the service, +back up, review the plan, apply, verify). Documentation only — releases nothing. diff --git a/docs/MAINTENANCE.md b/docs/MAINTENANCE.md index 406a3d3e5..73b346b11 100644 --- a/docs/MAINTENANCE.md +++ b/docs/MAINTENANCE.md @@ -65,6 +65,39 @@ 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 — orphan 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 no longer described by any metadata — usually companion columns an older +platform version provisioned automatically and the new version no longer does. +Orphan columns are harmless (the runtime never reads or writes them), so this +cleanup is **deferred by design**: schedule it for a maintenance window instead +of bundling it into the upgrade itself. + +**Known case — ObjectStack 17.0 `__search` orphans +([#528](https://github.com/objectstack-ai/hotcrm/issues/528)).** 17.0 tightened +the conditions under which full-text `__search` companion columns are +provisioned, so a database that lived through ≤16.x carries 9 orphan `__search` +columns after the upgrade (`crm_competitor`, `crm_opportunity_line_item`, +`crm_quote_line_item`, `crm_task`, and `sys_metadata`, among others — the full +list is whatever `os migrate plan` prints for your database). + +Cleanup procedure: + +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. Every destructive entry must + be a column you expect to drop; if anything else appears (a column backing a + field you still declare), stop and fix the metadata drift instead. +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 From 4018314571d58f0031bf07d4324a6acc8c01a44e Mon Sep 17 00:00:00 2001 From: yinlianghui Date: Thu, 30 Jul 2026 16:23:53 +0800 Subject: [PATCH 2/2] docs(maintenance): flip the 17.0 __search case from cleanup target to known false positive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue #528 was reclassified after column-level verification: the 9 database-only `__search` columns are live pinyin search-companion columns provisioned by the dev runtime, not orphans. `os migrate plan` misreports them because the CLI's schema view differs from the running server's (upstream objectstack#3955), so the original runbook would have talked operators into dropping working search columns — and its own safety check could not catch it, since the section named those columns as expected-to-drop. Section 3.1 now leads with the distinction (genuine orphan vs planner-invisible live column), records the __search class as a do-not-touch false positive until #3955 lands, and hardens the plan-review step with a second test plus a concrete proof technique (diff against a freshly created database from the same artifact). Co-Authored-By: Claude Opus 5 --- .changeset/orphan-search-column-runbook.md | 18 ++++-- docs/MAINTENANCE.md | 67 ++++++++++++++++------ 2 files changed, 60 insertions(+), 25 deletions(-) diff --git a/.changeset/orphan-search-column-runbook.md b/.changeset/orphan-search-column-runbook.md index dcfc0bb07..f385ecb64 100644 --- a/.changeset/orphan-search-column-runbook.md +++ b/.changeset/orphan-search-column-runbook.md @@ -1,9 +1,15 @@ --- --- -Document the cleanup runbook for orphan `__search` companion columns left behind -when ObjectStack 17.0 tightened search-column provisioning (#528): a new -"Destructive schema drift" section in `docs/MAINTENANCE.md` covering when -`os migrate plan` reports database-only columns, the known 17.0 `__search` case, -and the safe `os migrate apply --allow-destructive` procedure (stop the service, -back up, review the plan, apply, verify). Documentation only — releases nothing. +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 73b346b11..1f0cfc96f 100644 --- a/docs/MAINTENANCE.md +++ b/docs/MAINTENANCE.md @@ -65,32 +65,61 @@ 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 — orphan columns after an upgrade +### 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 no longer described by any metadata — usually companion columns an older -platform version provisioned automatically and the new version no longer does. -Orphan columns are harmless (the runtime never reads or writes them), so this -cleanup is **deferred by design**: schedule it for a maintenance window instead -of bundling it into the upgrade itself. - -**Known case — ObjectStack 17.0 `__search` orphans -([#528](https://github.com/objectstack-ai/hotcrm/issues/528)).** 17.0 tightened -the conditions under which full-text `__search` companion columns are -provisioned, so a database that lived through ≤16.x carries 9 orphan `__search` -columns after the upgrade (`crm_competitor`, `crm_opportunity_line_item`, -`crm_quote_line_item`, `crm_task`, and `sys_metadata`, among others — the full -list is whatever `os migrate plan` prints for your database). - -Cleanup procedure: +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. Every destructive entry must - be a column you expect to drop; if anything else appears (a column backing a - field you still declare), stop and fix the metadata drift instead. +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.