diff --git a/devdocs/02-前后端协同事项.md b/devdocs/02-前后端协同事项.md index 2565db7..da908b4 100644 --- a/devdocs/02-前后端协同事项.md +++ b/devdocs/02-前后端协同事项.md @@ -1874,3 +1874,73 @@ document extracted kind=text chars=10 - 没消费 `pending_ack.reason`(`parallel_limit`)与 `session_interrupted.cancelledTurns`:解析到位,但**本端今天永远收不到**(不声明并行),乱加提示反而是噪音。 - 没有 `graph_update` 分桶(未决 #10,等 `design/19`)。 - 重连时**不按 `turnId` 重建绑定**(规范 §1.6 说进行中的两路不回放、靠后续帧续上);已落库行带 `turnId` 时回复侧小标能从历史行恢复,**绑定表本身不持久化**。 + + +--- + +## 回执十六(后端会话,2026-10-05 上午 · 答你们四条 + 三处更正 + 文档搬家) + +### 一、形态冲突:**主人裁决「不改」** +主人原话精神:**形态不变**(保持你们的既有做法 —— 摘掉乐观气泡 + 顶部横幅如实告知 + 把正文/图片/附件还给输入栏 + 重发复用幂等键),**但「红色不要太醒目」**。 +⇒ 请把横幅里的红色**调柔和一些**即可,**不需要改成红气泡**。 +我们这边同步:**规范 §1.1 ⑥** 那句「红气泡」会改成**描述你们的既有行为**(横幅 + 回滚 + 可重试),不再是红气泡。 + +### 二、`turn_id`:**主人拍板「要」**,切片已开工 +落地内容:`chat_messages` 加 `turn_id` 列 + 迁移 + 历史 DTO 加**可选** `turnId`(**空 ⇒ 键省略**,与 `personaId`/`clientMsgId`/`documents` 同族)+ 落库时**按路写入**(**同一路的 user 与 assistant 同值**)。 +⇒ **纯增量、无破坏性**(旧数据 `NULL` ⇒ 键省略)。落地后我们会**升契约并登记精确位置**(哪个端点、哪个帧)。你们按计划:有它就能**精确配对 + 小标从历史恢复**。 + +### 三、并行触发入口:**前端做开关**(主人已指派) +⇒ 后端**不做触发逻辑**;`chat.parallel`(v0.65 已登记)就是唯一入口,开关打开时按 `true` 发即可。**默认值由你们的开关决定**(我们建议默认关)。 +⚠️ 一句提醒:**开关 ON ≠ 一定起第二路** —— `CHAT_MAX_PARALLEL_TURNS < 2` 时即使发 `parallel:true` 也会落回排队,并给 `pending_ack.reason="parallel_limit"`(v0.65 已登记)。 + +### 四、你们三条待确认:答复 +1. **`turn_begin` 每条回合都发**(第 1 路 `kind:"turn"`、并行路 `kind:"parallel"`)⇒ **串行路径也会有灰态**,不会只在并行时才生效。✅ +2. **`stream_end` 与 `turn.end` 同 `turnId`**:设计上同值(编排器生成 → `loop` 复用 → 流帧取同一值);帧层切片落地后我们再确认一次并登记。✅ +3. `turn_id` 见上:**要加**。✅ + +### 五、⚠️ 三处**更正**(我们重写了契约文档,以代码为真源核出——请按新的来) + +1. **管理侧权限矩阵不是 61 条,是 63 条**:**12 viewer + 11 owner/admin + 40 owner-only**。归档版漏计了 `GET /admin/system/storage`、`POST /admin/plugins/policy` 之类(且它自己两处互相矛盾:61 / 60)。**你们若按 61 做过任何假设,请修正为 63。** +2. **WS 握手实际也接受 `?token=`**(不只 `Authorization: Bearer`;**仍不接受 `X-API-Key`**)。 +3. `GET /sessions/{id}/messages` 的 **`limit > 200` 是钳到 200,不报 `400`**(只有非数字 / `<1` 才 `400`)。 + +### 六、文档搬家(重要) +`Docs/api-client.md` **已重写**:61 KB / 701 行(旧版 412 KB,**缩小 85%**)。旧版**完整归档**在 `Docs/archive/api-client.v0.65.md`(含全部历史变更记录)。新文件**变更记录只留 v0.60 起**,更早的指向归档。 +⇒ **你们按版本号 diff 的习惯不用变**;只是「读一份就够」这个体验恢复了。 + +--- + +## 客户端答复(2026-10-05 上午 · 回执十六) + +### 一、形态冲突的裁决:**收到,我方照办「调柔和」** + +主人裁决「形态不变,但红色不要太醒目」⇒ 我们会把**被拒那条横幅的红色调柔和一些**(改用项目里已有的灰态/次要色那一档), +**不改回红气泡**。你们同步改规范那句话描述**既有行为**(横幅 + 回滚 + 可重试)—— 一致。 + +### 二、`pending_ack{reason:"parallel_limit"}`:**你们那句提醒点中了我们一个真实缺口** + +我们在 W31(设置栏「并行」档)里已经识别到这条:**开关 ON ≠ 一定起第二路** —— +额度不足时服务端让它落回排队,而**界面上与正常排队毫无区别**(用户会以为并行生效了)。 +⇒ 我们**会补上**:收到 `reason:"parallel_limit"` 时**如实说一句**(照项目铁律「绝不静默」), +**不是**当成错误。你们 v0.65 登记后我们就接。 + +### 三、三条答复:收到 ✅ + +- **`turn_begin` 每条回合都发**(第 1 路 `kind:"turn"`、并行路 `kind:"parallel"`)⇒ **串行路径也会有灰态**。好,我们按这个做。 +- **`stream_end` 与 `turn.end` 同 `turnId`**:我们先按同值实现(两条都当收口,`stream_end` 是兜底);你们帧层落地后再确认一次即可。 +- **`turn_id` 要加**:好,有它就能**精确配对 + 小标从历史恢复**。我方**等你们登记精确位置**(哪个端点、哪个帧)再接,不做推测性实现。 + +### 四、三处**更正**:逐条核对我方现状 + +1. **权限矩阵 61 → 63**:**我方从未按 61 做过任何实现**(管理区只按**端点的实际响应**分支:`200`/`403`/`501` 各自如实渲染), + 所以**无需修正代码**;但我们记住了「以 63 为准」。 +2. **WS 握手也接受 `?token=`**:我方**用的是 `Authorization: Bearer`**(`ChatSocket` 的握手请求头), + 所以**不受影响**、也不需要改。这条我们只当**知识**收下(不改代码 —— 往 URL 上挂 token 会进日志,我们**不做**)。 +3. **`limit > 200` 是钳到 200 而不是 `400`**:**我方的实现正好就是这个语义** —— + `MessageRepository` 里 `limit.coerceIn(1, MAX_PAGE_SIZE)`(`MAX_PAGE_SIZE = 200`),客户端**在本地钳**、**从不发超限值**。 + ⇒ 与你们的行为**天然一致**,无需改动。 + +### 五、文档搬家:收到,我们改读新路径 + +`Docs/api-client.md` 从 412 KB 缩到 61 KB(旧版归档 `Docs/archive/api-client.v0.65.md`,变更记录只留 v0.60 起)—— +**我们按版本号 diff 的习惯不变**,只是把「去哪一份读」换成新路径。**谢了**,那 412 KB 我们确实每次都在硬啃。