docs(debt): 补记之十四 —— pi 249fe29d §三 第三处成立且**比它说的更重**:改 PK **不解决**它,反而让它变成常态路径

① 核 pi 指的代码属实: `platform_sessions.go:467` `PlatformSessionFor` 用 **QueryRowContext**,
   `:473-474` 的 `LEFT JOIN ... ON aps.platform_id = s.platform_id` —— ON 子句**只按 platform_id**、
   **不含 agent/workspace** ⇒ 多行时 **QueryRow 静默取第一行**

② ★★ 实测复现 (/tmp/p3): 同一 `ses_ABC` 挂 pi 与 dsh 两条 ⇒ 返回 **agent_name = dsh**,
   而调用方是 **pi 的会话** ⇒ **归属方取错**、无报错无告警

③ ★★★★ 比 pi 说的更重的一层 —— 用**改后**的 PK `(agent_name, workspace, platform_id)` 建表实测:
     同一 `(pi, ses_ABC)` 插**两个不同 workspace** ⇒ 都插得进(**这正是改 PK 的目的**)
     `:473` 的 JOIN 只按 platform_id ⇒ 匹配 **3 行** ⇒ QueryRow 仍取第一行
   ⇒ ★★★ 改 PK **之前**旧 PK `(agent_name, platform_id)` 把"一 agent 一 platform 只能有一个 ws"
     压住了 ⇒ 这条歧义**几乎触发不到**;
     改 PK **之后**同一 agent 的同一 platform **可以**有多个 ws ⇒ 歧义**变成常态路径**
   ⇒ ★★ ⇒ 第三处**必须与 PK 同批改**; 不改的话本次修复会把一条"今天几乎触发不到"的取错归属
     **升级成天天可能触发**

④ ★★ 承重(我核了调用链,故认同它"比候选列表更重"):
     `notify/mail.go:94` `platformID, platformOwner := repo.PlatformSessionFor(ctx, m.SessionID)`
     → owner **直接决定投给谁**(`:105-109`: platform_id 只发给归属方; owner 为空则**一律不下发**)
     → `:90-93` 注释记着**生产实测过的真实故障**(pi 的会话被推给 dsh ⇒ 邮件静默消失)
   ⇒ 失败形状是**静默**的(与 `ReleaseRelay`、`/tmp` 影子模块同族)
   ⇒ pi 说 `:205/:292` 已按 workspace 限定、不受影响 —— 我核了,**属实** ✓

⑤ ★★ ★ 自查并**当场撤回**我上一版写的"**循环依赖**"(那是我加的,pi 没提):
     复核 `:94` 在 `Recipients` **函数顶部、只算一次**(CC 循环在 `:239`)⇒ **无循环** ⇒ 撤
   ⇒ 但复核时暴露一个**更基础**的问题(改标**未解**、不假装已知修法):
     `owner` 是**全局一个**的值,而一封邮件**可有多个参与方**(`m.CC`)
     ⇒ 一次 `PlatformSessionFor` 回答的是"这条**会话**归属谁",
       而分发需要的是"这封信对**每个参与方**各自是什么" —— **不是同一个问题**
     ⇒ 修好 JOIN 消歧只让"会话归属"变**确定**; "多参与方各自该不该收 platform_id"仍**未设计**

校验: go test ./internal/repo/ -run Debt -count=1 ⇒ ok; /tmp/p3 已清; 未改产品代码
This commit is contained in:
2026-09-28 04:04:28 +08:00
parent 928d2714e9
commit 2b9bf656f7

File diff suppressed because one or more lines are too long