# 会话哑掉线 · 收尾报告(2026-10-01) > 线:多会话协作机制 · **会话哑掉** | 本棒会话:`345e6839`(自动化 · 阈值交接棒) > 范围:上棒遗留 **4 项**(一件一件做,做完即停) | 并发锁:本轮持**全局独占**(机制层),**已释放** ## 一、结论(先读) | # | 遗留项 | 结果 | |---|---|---| | 1 | `selftest.py` 补「会话哑掉」回归用例 | ✅ 已补 2 条;`python selftest.py` **PASS 37 / FAIL 0**(基线 35 + 新增 2) | | 2 | `board_ext.py` 清退役文案 | ✅ 已清 **2 处**(详见 §三) | | 3 | 已哑主会话 `f8a792ab` 的归属 | ⛔ **未动** —— 取证后**判不准**,按任务书报用户(详见 §四) | | 4 | 报告 + 释放锁 | ✅ 本报告 + 锁已 `--release-exec` | ## 二、遗留 1:回归用例(⛔ 不碰生产) 新增到 `~/.workbuddy/skills/multi-session-collab/scripts/selftest.py`: | 用例 | 判据 | |---|---| | 哑会话:诊断日志 ≥ 9.5 MiB ⇒ `_deaf_sids()` 判哑 | ① ≥9.5 MiB 判哑 ② **9,499,999 B(差 1 字节)不判哑**(边界)③ KB 级不判哑 ④ 结果**恰好只有那一条** | | 哑会话:投递命中已哑目标 ⇒ `skipped=target-deaf` | ① 判读=`target-deaf` ② 投递台账**零增量** ③ 已把喊话落到 `NEED-USER.md` | **怎么做到不碰生产**:`_deaf_sids()` 每次调用**现读** `CODEBUDDY_CONFIG_DIR` ⇒ 用例把它临时指到 `tmp/selftest/fake-cfg/`(用 `truncate` 建**稀疏文件**拿到 10 MiB 体积读数,不真写),跑完 **finally 还原** (`_db()` 也读这变量,不还原会把后面的用例一起带偏)。元用例「本测试不碰生产」仍全绿。 ## 三、遗留 2:`board_ext.py` 清掉的 2 处退役文案 文件:`E:/ProgramData/AIProject/ai1net-dsh-server/.workbuddy/collab/board_ext.py` | 处 | 原文 | 改成 | 为什么 | |---|---|---|---| | 文件头 | `「手机接入」线的看板扩展` | `「本机协作」的看板扩展` | 「手机接入」是**已退役线名**(用户 2026-09-30:「后面也不叫手机接入」);现行目标名=`goal.json` 的「本机协作」 | | chip① 标题 | `dsh-client · 设备接入 · 本地中转 :20090` | `dsh-plugin-ai1net · 设备接入 · 本地中转 :20090` | `dsh-client` 在此是**退役包名**:原 `@dsh-client/device-shim` 已退役,2026-09-29 归并进 `dsh-plugin-ai1net/lib/device-access.js`(模块⑥「设备接入」)⇒ 换成现承载方真名 | **验证**:`py_compile` 过;直调 `build()` 打印面向用户文案 ⇒ **「手机接入」0 命中、「垫片」0 命中**,chip① 已显示新系统名。 **保留未动**(判据=有客观可判的取舍,⛔ 不擅自扩大): - 「本地中转」是 2026-09-30「说人话」批量替换**有意**定下的说法(术语对照表 `| 本地反代 | 「本地中转」 |`)⇒ ⛔ 改回去会推翻那次决定。 - chip② `dsh-client · 覆盖网络节点 · 中继客户端` 属**另一条组件**(覆盖网络线),其系统名本轮**未取证** ⇒ ⛔ 不确定不改。 - `:20090` 裸端口是**真读数**(`netstat`/日志真值),⛔ 不是抽象词 ⇒ 保留。 ## 四、遗留 3:`f8a792ab` 归属 —— ⛔ 未动,报用户 **取证(只读)**: | 会话 | 标题 | 状态 | 诊断日志 | |---|---|---|---| | `f8a792ab` | 检查多会话协作机制并修复问题 | completed(08:55) | 今天 **10.0 MB ⇒ 已哑** 🔴 | | `a202550c` | 主控 · 唤醒机制线 · … | completed(07:13) | 昨天 10.0 MB 🔴/**今天 4.2 MB(已轮转恢复)** | | `345e6839` | 主控 · 协作机制 · 阈值交接收尾 | working | 1.8 MB ✅ | - 协作登记 `collabd-state.json` 里 `roles.main = f8a792ab`(**陈旧**,它已结束且已哑)。 - **本会话 `345e6839` 就是唯一带「主控」前缀的活会话,而它是自动化会话**(`is_background_automation=1`,用户看不见、**不可接续**)。 ⇒ **工作区当前没有任何「活的且可接续」的候选主会话** ⇒ 改认谁都是错的(把通知投给用户看不见的会话 = 比现在更糟)。 ⇒ 按任务书「判不准 ⇒ 报用户,⛔ 不要自己动」:**本轮未改 roles、未归档任何会话**(宿主库 `sessions` 属活库,⛔ 更不碰)。 **🔴 需用户拍板的一件事**(下一节单列)。 ## 五、本轮**新发现**(不在清单内,⛔ 未动手,仅登记) `_deaf_sids()` 扫的是 **今天+昨天** 两个日期目录。而宿主日志是**按天分目录**(`logs/<日期>/sdk/conversations/`), **昨天的文件是历史、永远不会再增长** ⇒ 任何「**昨天撞过 10 MiB 上限、今天已正常轮转**」的会话,会被**永久判哑**。 - 实证:`a202550c` 昨天 10,485,758 B、**今天 4,354,629 B(宿主今天仍在写它)** ⇒ 它**没哑**,却仍被判哑。 - 后果是**方向相反的新假绿**:本该投递的目标被拦下 ⇒ 通知落空。09:12 那条 `NEED-USER`(称 `a202550c` 已哑)即由此而来。 - 判据收窄的取舍(留给下一棒/用户):只认**最新一天**(今天)—— 好处是消除上述假阳性;代价是「跨天后还没写过日志」的会话当天扫不到(它当天没写 = 也没在增长,影响有限)。 - ⛔ 本轮**未改**:超出 4 项清单,且属机制层(改后须重跑 selftest)。 ## 六、上一棒改动清单(⛔ 未 commit) | 文件 | 改动 | |---|---| | `~/.workbuddy/skills/multi-session-collab/scripts/selftest.py` | +2 用例(+`_fake_cfg`/`_with_fake_cfg` 工具 + `DEAF_MIB` 常量) | | `…/.workbuddy/collab/board_ext.py` | 2 处退役文案 | 锁状态:上一棒持全局独占 → 已 `--release-exec "会话哑掉收尾"`。 ## 七、本棒(接续会话)已修:`_deaf_sids()` **永久判哑**(2026-10-01 09:2x–09:4x) > 来源:本线**接续会话**(上一棒 `345e6839` 达阈值按 §1.5 G 开的接续会话,顶替主会话职能)。 > 锁:机制层 ⇒ **全局独占**。 **改了什么**(`~/.workbuddy/skills/multi-session-collab/scripts/collabd.py::_deaf_sids()`): | | 旧 | 新 | |---|---|---| | 判据 | 今天+昨天 **任一** ≥9.5 MiB ⇒ 判哑 | **每个会话取它最新一天**那份日志 ≥9.5 MiB ⇒ 判哑 | - 🔴 **旧写法的病**:宿主会话日志**按天分目录**、昨天那份**永不再增长** ⇒「昨天撞过上限、今天已正常轮转」的会话被**永久判哑** ⇒ 本该投出去的唤醒被**静默拦下**(方向相反的新假绿)。 - ⚠️ **今天的兜底保留**:某会话今天**没有**日志(宿主今天没写过它)⇒ 仍退到昨天那份判 —— 比"直接放行"保守。 **生产数据实测(只读,⛔ 未跑 `--tick`)**: - 旧判据 7 条哑 → 新判据 6 条哑;**唯一被收窄掉的正是 `a202550c`**(昨天 10.49 MB / 今天 4.35 MB,宿主仍在写)✅ - `f8a792ab`(今日 10,485,715 B)**仍判哑** ✅ —— 第一轮那道闸没有被削掉。 - **副作用面=零**:主会话解析(旧 vs 新)**结果逐字相同**(`5f607d3e` · `prefix:主控`)。 **回归用例**(`scripts/selftest.py`):+1 条「只认最新一天」(昨 10 MB/今 4.2 MB ⇒ ⛔ 不判哑;今天才撞上限 ⇒ 判哑;只有昨天 ⇒ 仍判哑;含**精确集合**断言)。 - `_fake_cfg()` 增 `day_offset`(0=今天 / 1=昨天);`_prepare()` **每轮清空** `tmp/selftest/fake-cfg`(防"第二轮才红"的跨轮污染)。 - ✅ `python selftest.py` ⇒ **PASS 38 / FAIL 0**(基线 37 + 新增 1);连跑两次均绿。 **顺带修掉一条环境假红**(⛔ 与本次改动无关,是既有用例的前提覆盖不全):「僵尸对账」用例只靠 `adopted` 反推前提的**一半**, 而僵尸分支的完整前提是「除观察者外无人在跑」**∧「无 IN_PROGRESS 运行」** ⇒ **自动化会话跑本自测时**,它**自己**的 automation run 必然 `IN_PROGRESS` ⇒ 前提不成立却不 SKIP ⇒ **必然假红**(实测:收窄前整套 36/1,只有本条红)。 ⇒ 照该用例既有的"前提不成立就 SKIP"姿势**补上后一半**(纯测试侧,⛔ 未动生产逻辑)。 ### 七-b 本棒改动清单(⛔ 未 commit) | 文件 | 改动 | |---|---| | `multi-session-collab/scripts/collabd.py` | `_deaf_sids()` 判据收窄为「每个会话只认最新一天」 | | `multi-session-collab/scripts/selftest.py` | +1 用例「只认最新一天」;`_fake_cfg(day_offset)`;`_prepare()` 清 `fake-cfg`;僵尸用例补 SKIP 前提 | ⛔ **未做**(不属本棒清单,如实挂着):未 `commit`/`push`(未获指令);未把技能本机→服务器单向推(改的是**技能**, 两处落点 `~/.workbuddy/skills/` 与服务器副本需 md5 双端一致,留给后续按 §1.5 B 阶段 5 处理)。 锁状态:本棒持全局独占(机制层 `--claim-exec "5f607d3e"`)→ 已 `--release-exec "5f607d3e"`;服务器侧那把 `storyforge-e2e` 操作锁**属他人**,按 R9 未动。