由 domain:engine 执行席落卡(session session_01VK8rFDtg8eREaxBGX99Csn)。这是 #11535 被切下来的那一半 —— 那张卡是真实客户报障(作者 baozhoutao,生产库,已手工抢修)。检测半边我已经派发(见 #11535 的认领评论),自动迁移半边不是我能裁的:它动的是存量数据的迁移形状,是破坏性且难回滚的动作,按协议恒交维护者。
一句话问题
一个用了很久的客户,把某个字段从「选一个人」改成「选多个人」,重启之后系统什么都没说,然后开始把「张三、李四」当成一整串文字塞进原来只放一个人的那一格里;自动化流程再把这一整串当成一个人名复制到子记录上,指向一个根本不存在的人。全新装的系统没这个问题,只有真实运营了一段时间的客户会中。现在的问题是:平台发现这件事之后,该不该自己动手改客户数据库的表结构。
前提(带 re-check 命令,复升级时逐条重跑)
三条路,和各自客户能感觉到的代价
| 做什么 | 客户感觉到的代价 |
|---|
| A | 平台在启动时自己改列(顺带删掉旧索引再建) | 客户不用做任何事,重启就好了。⚠️ 但平台在没人看着的时候动了生产库的表结构 —— 改错了、或中途断了,恢复要靠备份。一次失败就是一次停机事故,而且是我们造成的。 |
| B | 只报警,要人来动 | 平台绝不碰客户的数据。⚠️ 但客户改个字段就撞上一条「请手工迁移」的错误,得会写 SQL 才能继续;不会写的就卡在那儿,或者干脆忽略警告继续跑,继续坏数据。 |
| C | 报警 + 给一条客户自己敲的迁移命令(不在启动时自动跑) | 客户要多敲一条命令,但是他自己按下的按钮、自己挑的时间(可以先备份、挑半夜)。⚠️ 我们要多维护一条命令和它的回滚说明。 |
业务含义直译:A = 「平台替你做主,像自动更新」;B = 「平台只当报警器,像体检报告说『你去做手术』」;C = 「平台给你一把有说明书的手术刀,你自己决定什么时候用」。C 是数据库产品里最常见的做法(和 Salesforce 改字段类型要走一个显式的转换向导、而不是后台偷偷改,是同一个姿态)。
os-decision-facets
- 实际业务拉动:今天就有客户撞上了,而且是已经在生产环境流血的那种 —— 数据已经坏了、已经手工抢修过一轮。三条路都解决「以后不再静默变坏」,区别只在谁来动手。所以这一轴推「必须做点什么」,但不偏向 A:客户要的是「别再悄悄坏」,不是「你替我改库」。
- 项目长远合理性:A 会让平台永久背上「自动改客户表结构」这个承诺 —— 今天是一种字段变更,明天客户会问为什么另外七种不自动改,特例只会增生。C 把能力做成一条显式命令,边界清楚,不承诺「所有变更都自动」。⇒ 偏 C。
- 防 AI 写代码犯错:这一轴看的是出错时谁看到什么。现在最坏:没人看到任何东西,数据默默坏掉,几周后才在一张子记录上发现指向不存在的人 —— 这正是「静默容忍」最贵的形态。A 也有静默的一面:改库成功了没人知道,失败了才知道。B 和 C 都是响亮拒绝:出错当场有人看见,而且看见的是「这张表这一列要迁移」而不是一句含糊的报错。⇒ 强推 B/C,反对把「自动改了」当成默认的安静路径。
- 创业阶段不扩散需求:A 是一个新的平台级能力(迁移引擎 + 索引重建 + 失败回滚),是这三条里最大的一件;而它服务的场景是「改字段类型」,不是每天发生的事。C 的成本接近 B 加一条命令。⇒ 反对 A,偏 B 或 C。
推荐:C(报警 + 显式迁移命令),回退 B(只报警)。 四轴里没有一条支持 A 作为自动、无人值守的行为;A 真正值钱的部分(那段 SQL 怎么写才对)在 C 里同样交付,区别只是谁按下按钮。
⚠️本分析看不见什么(强制置信缺口):① 我不知道有多少客户手上是「长期运行库」,也就是说不知道这件事一年会发生 3 次还是 300 次 —— 如果是 300 次,B 的人工成本会难看得多,A/C 的性价比要重算;② 三条路都不修已经坏掉的数据。报障人除了改列,还得「repair the child rows that had already been corrupted」—— 那是第四件事,谁都没覆盖,可能需要单独一张卡;③ 我没有实测 A 在有外键/有索引/有大表时的失败率,那是最能决定 A 是否可接受的数字,而我没量。
裁后我会怎么执行(你不用管)
低摩擦裁决格式:回一个字母即可;「C,但 X」我会按 C 落地并把 X 记进卡里;不回等于留在箱里,不会有人替你裁。
相关
⚠️ 落卡说明:本卡首版用协议规定的 HTML 注释标记 os-decision-facets 写四棱块首行,回读发现被 sanitizer 吃掉(与本轮两个 dev 的报告标记同一失效模式)。已改为字面文本重写 —— 协议本身规定提取按字面文本 grep、不依赖注释形状,所以字面写法是合规的,并且是唯一能存活的写法。
由
domain:engine执行席落卡(sessionsession_01VK8rFDtg8eREaxBGX99Csn)。这是 #11535 被切下来的那一半 —— 那张卡是真实客户报障(作者baozhoutao,生产库,已手工抢修)。检测半边我已经派发(见 #11535 的认领评论),自动迁移半边不是我能裁的:它动的是存量数据的迁移形状,是破坏性且难回滚的动作,按协议恒交维护者。一句话问题
一个用了很久的客户,把某个字段从「选一个人」改成「选多个人」,重启之后系统什么都没说,然后开始把「张三、李四」当成一整串文字塞进原来只放一个人的那一格里;自动化流程再把这一整串当成一个人名复制到子记录上,指向一个根本不存在的人。全新装的系统没这个问题,只有真实运营了一段时间的客户会中。现在的问题是:平台发现这件事之后,该不该自己动手改客户数据库的表结构。
前提(带 re-check 命令,复升级时逐条重跑)
git grep -n "widen_varchar\|narrow_varchar\|drop_column\b" origin/main -- packages/drivers/driver-sql/src/schema-drift.ts(2026-08-24 实测:
schema-drift.ts:129-133,五种动作,无基类型变更)Part ofPR 是否已合。本卡的选项 B 就是「检测半边即终局」,所以先确认它到底落成了什么。三条路,和各自客户能感觉到的代价
业务含义直译:A = 「平台替你做主,像自动更新」;B = 「平台只当报警器,像体检报告说『你去做手术』」;C = 「平台给你一把有说明书的手术刀,你自己决定什么时候用」。C 是数据库产品里最常见的做法(和 Salesforce 改字段类型要走一个显式的转换向导、而不是后台偷偷改,是同一个姿态)。
os-decision-facets推荐:C(报警 + 显式迁移命令),回退 B(只报警)。 四轴里没有一条支持 A 作为自动、无人值守的行为;A 真正值钱的部分(那段 SQL 怎么写才对)在 C 里同样交付,区别只是谁按下按钮。
裁后我会怎么执行(你不用管)
domain:engine队列(命令 + 干跑 + 回滚说明 + 文档),driver-sql: single→multi-value field change never migrates the existing column type (varchar/text kept), arrays silently stored as stringified literals — data corruption on Postgres #11535 待检测半边落地后转pm:blocked指向它。低摩擦裁决格式:回一个字母即可;「C,但 X」我会按 C 落地并把 X 记进卡里;不回等于留在箱里,不会有人替你裁。
相关
Part of,⛔ 不Fixes)packages/drivers/driver-sql/src/schema-drift.ts:129-133os-decision-facets写四棱块首行,回读发现被 sanitizer 吃掉(与本轮两个 dev 的报告标记同一失效模式)。已改为字面文本重写 —— 协议本身规定提取按字面文本 grep、不依赖注释形状,所以字面写法是合规的,并且是唯一能存活的写法。