You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Filed by the domain:engine seat (session session_01VK8rFDtg8eREaxBGX99Csn) as the execution half of the #11700 ruling.
The ruling this card executes
Maintainer, 2026-08-24, live PM chat. ⛔ Quoted verbatim, not translated:
「11700 11693 不需要考虑历史数据,其他按照你的建议继续」
Ruled: C — the platform warns and ships an explicit, operator-run migration command. ⛔ It is never run automatically at boot. Option A (unattended auto-migration) is rejected: on the four facets it was the only route that has the platform altering a customer's production table structure with nobody watching. B (warn only) survives solely as C's degenerate form if this command is never shipped — which is what this card exists to prevent.
⚠️Amendment, from the ruling's own words — historical data is OUT of scope. The #11700 analysis carried a confidence gap flagging that no route repairs rows corrupted before detection landed. That rider is struck: rows already corrupted are the customer's to repair, as the reporting customer already did. ⛔ Do not add backfill or row-repair to this command, and ⛔ do not file a card for it.
What to build
A command an operator runs deliberately, on their own schedule, having taken their own backup. The shape the ruling names:
the migration itself — the dialect-correct statement for moving a stale varchar/text column to json when its field declares multiple: true;
a dry run — show exactly what would run, and against what, without running it;
rollback notes — what the operator does if it goes wrong;
docs — so the finding's message can point at something real.
⚠️The SQL is already written and, more importantly, already measured. PR #11720 (the detection half) emits the remedy statement per dialect, and its suite executes that statement against live Postgres 16.13 and MySQL 8.0.46 over four row states, asserting the finding then clears. ⛔ Do not re-derive it — reuse it, and read the two corrections that were forced by measurement rather than preference:
to_json(col) turns a legacy single value into a JSON scalar under a now-multi-value field (Array.isArray === false), so json_build_array is used instead, which makes Postgres agree with MySQL's JSON_ARRAY;
json_build_array(NULL) is [null] — a one-element array — so an explicit IS NULL arm exists. It was added after the version without it was run on the live server and observed to give every NULL row a value.
Dialect scope, measured: the corruption occurs on Postgres and MySQL and does not on SQLite (which reads back a real array). SQLite is excluded.
⚠️ Confirm where the command belongs. The drift finding lives in packages/drivers/driver-sql, but an operator-facing command plausibly lives in packages/cli next to the other os migrate surfaces — which is another seat's lane. If it lands there, this becomes a cross-lane card and goes to that queue, ⛔ not into a driver-sql PR. Establish the landing point before writing code; it may change this card's domain:*.
⛔ The reconciler must still not execute this op automatically. fix(driver-sql): report a multi-value field left on a stale varchar/text column #11720 ships manual_column_type_change with no reconciler arm by design; this command is a separate, deliberate path the operator invokes. A change that quietly gives the op an arm would convert C back into A, which was rejected.
Filed by the
domain:engineseat (sessionsession_01VK8rFDtg8eREaxBGX99Csn) as the execution half of the #11700 ruling.The ruling this card executes
Maintainer, 2026-08-24, live PM chat. ⛔ Quoted verbatim, not translated:
Ruled: C — the platform warns and ships an explicit, operator-run migration command. ⛔ It is never run automatically at boot. Option A (unattended auto-migration) is rejected: on the four facets it was the only route that has the platform altering a customer's production table structure with nobody watching. B (warn only) survives solely as C's degenerate form if this command is never shipped — which is what this card exists to prevent.
#11700analysis carried a confidence gap flagging that no route repairs rows corrupted before detection landed. That rider is struck: rows already corrupted are the customer's to repair, as the reporting customer already did. ⛔ Do not add backfill or row-repair to this command, and ⛔ do not file a card for it.What to build
A command an operator runs deliberately, on their own schedule, having taken their own backup. The shape the ruling names:
varchar/textcolumn tojsonwhen its field declaresmultiple: true;to_json(col)turns a legacy single value into a JSON scalar under a now-multi-value field (Array.isArray === false), sojson_build_arrayis used instead, which makes Postgres agree with MySQL'sJSON_ARRAY;json_build_array(NULL)is[null]— a one-element array — so an explicitIS NULLarm exists. It was added after the version without it was run on the live server and observed to give every NULL row a value.Dialect scope, measured: the corruption occurs on Postgres and MySQL and does not on SQLite (which reads back a real array). SQLite is excluded.
Before dispatching, re-verify rather than assume
manual_column_type_changeis emitted onorigin/main(
git grep -n "manual_column_type_change" origin/main -- packages/drivers/driver-sql/src/schema-drift.ts).packages/drivers/driver-sql, but an operator-facing command plausibly lives inpackages/clinext to the otheros migratesurfaces — which is another seat's lane. If it lands there, this becomes a cross-lane card and goes to that queue, ⛔ not into a driver-sql PR. Establish the landing point before writing code; it may change this card'sdomain:*.manual_column_type_changewith no reconciler arm by design; this command is a separate, deliberate path the operator invokes. A change that quietly gives the op an arm would convert C back into A, which was rejected.Related
pm:blockedpointing here once the detection half landsneeds_confirmand be declined by the reconciler #11721 — the drift category vocabulary has no report-only state (the op currently borrowsneeds_confirm)