docs(debt): 补记之十七 —— ★★★★ 实测发现 ②**已上线而 ①③④ 未改**:本条"尚未实现修法"已过期,且半修引入①一个潜伏互斥

① ★ 本条目的「尚未实现修法」**已过期**(`kind` 仍 scope)—— 实测部署二进制:
     `/opt/agentmail/agentmail-gateway` `vcs.revision=359cb436…`(**09-28 10:14:52** 启动, PID 2824864)
     `db640e2`(09-26 14:20) 经 `git merge-base --is-ancestor` 判定**在 359cb436 里**;
     二进制含 `workspace IN (` ⇒ ★ **② 已在生产运行**
   ⇒ 与 `prune-artifact-evidence-decays-with-reboot` 同族: **记"状态"的话会过期**

② ★★★★ 四处必改点的**真实状态**(不是"全没改",也不是"改完了"):
     ① PK      `init_sqlite.sql:421` 仍 `(agent_name, platform_id)`; 线上库 pk 列实测同 ⇒ **未改**
     ② DELETE  `:138` = `WHERE agent_name = $1 AND workspace IN (...)` ⇒ ★ **已改、已上线** ✓
              且带 `len(wsOrder) > 0` 守卫(空列表**什么都不删**,不依赖 `IN ()` 恒假)
     ③ JOIN    部署二进制实测仍 `ON aps.platform_id = s.platform_id`(**单键**)⇒ **未改**
     ④ 迁移重建表 未找到 ⇒ **未改**
   ⇒ 我此前整体记成"未修"是**粗口径**; 真实是 **② 单独落地**(4 处里的 1 处)

③ ★★★★★ 由此产生一个新的**潜伏互斥**(② 单独上线使旧 PK 从"无害"变成"可撞"):
     旧代码(全量 DELETE)每轮清掉该 agent **所有 ws** ⇒ 同一 `(agent, platform_id)`
        **不可能跨 ws 残留** ⇒ 旧 PK 的 UNIQUE **永不触发**
     新代码(② scoped DELETE)只清**本次 list 覆盖的 ws** ⇒ list **之外**的 ws 行**留下**
        ⇒ 同一 `platform_id` 先在 `/w2`、本次又从 `/w1` 上报:
           DELETE 只清 `/w1`(`/w2` 行**留着**)→ INSERT `(agent, id, /w1)`
           ⇒ ★★★ 撞 `UNIQUE(agent_name, platform_id)`(实测: `UNIQUE constraint failed`)
     `INSERT` 是**普通 INSERT、无 `ON CONFLICT`**(`:155-160`)⇒ 错误经 `return err` 冒泡
        ⇒ 本次上报**整体失败、事务回滚** ⇒ 后果是**镜像停止更新**(非数据错乱)
   ⇒ ⇒ ★★ **② 单独上线不是"无害的部分修复"**: 它在旧 PK 未改的前提下,
     把"永远不会发生"的约束冲突变成"**条件满足即发生**"
   ⇒ 这是"必须同批"的**另一半**: 补记之十四/十五 讲"**PK 改了而 ③ 没改**会坏";
     这条讲"**③④ 没改而 ② 改了**已经上线、也会坏"

④ ★★ 当前**可达性**(严谨: 前置条件目前不满足 ⇒ 尚未实际发生):
     需 (i) 同 `platform_id` 的行**跨 ws 残留** + (ii) 该 id 在本次 list 里**重新出现**
     生产实测: 同 agent+platform 多 ws = **0**; 同 platform 多 agent = **0**
   ⇒ **当前无触发实例** ⇒ 本条是**潜伏**,不是"正在坏"
   ⇒ ⚠️ 但 dsh `collectSessions()` 返回**该 agent 所有会话**(cwd 取自各自 header)
     ⇒ 一个 dsh 进程**可以**持有多 cwd 会话 ⇒ (i) 在结构上**可达**;
     线上该表已有 **439 行** ⇒ 一旦某条会话 cwd 变化即命中
   ⇒ 定级: **潜伏 / 条件满足即发生**(不是理论上的,是**差一个 cwd 变化**)

⑤ ★★ 可判形状(并入 ⑩⁗ 家族):
     ① 报"修了/没修"**必须逐处报**,不能合成一个布尔 —— 我这次被自己的粗口径骗了
     ② 修复分片上线时**必须重算"旧不变量被谁依赖"**: 旧 PK 的 UNIQUE 安全依赖**旧 DELETE 的全量语义**;
        DELETE 变 scoped 后那份安全性**随之消失** ⇒ 两者是**隐式耦合**,不在类型/签名上
     ③ 判据须能区分"未修"与"**半修**": 我的 d1 判据转绿(只测 ②),而 ①③④ 仍红
        ⇒ ★ **判据集合必须与必改点集合一一对应**,否则"绿"会被读成"修好了"

校验: go test ./internal/repo/ -run Debt -count=1 ⇒ ok; /tmp/h2 已清
This commit is contained in:
2026-09-29 04:07:13 +08:00
parent b995f98077
commit 5a2aa057b9

File diff suppressed because one or more lines are too long