Files

294 lines
22 KiB
Markdown
Raw Permalink Normal View History

# 会话机制合并技能包 · 方案(2026-10-01)
> 状态:**正式方案(任务 0 产出)** · 补 `接续包_会话机制合并技能包_20261001.md §2 任务 0` 的后半段
> 落点:`$WS/交付物/会话机制合并技能包-方案-20261001.md`
> 定位:**本文件是任务 1–4 的唯一设计依据**;与接续包 §3.4 冲突时以本文件为准(本文件是 §3.4 的展开,⛔ 未改其任何已定项)
> ⚠️ 本文件只写**本轮实测到的**计数与字节;⛔ 不报未经实测的系数。
---
## 一、目标(用户原话拆解 · 不可改动)
> 「整合 会话机制相关技能为一个 skill 包,再将多会话协作机制也整合到这个技能包,要求换一台电脑的 workbuddy 上运行也能自动完成配置,让所有会话遵循会话机制,并且可独立使用多会话协作」
| # | 硬要求 | 判据(怎么算达成) |
|---|---|---|
| 1 | 一个技能包装下「会话机制」相关技能 | 目标目录存在,`SKILL.md` 能分别加载两段 |
| 2 | 多会话协作机制并入同一个包 | 协作本体脚本在包内,且包内可独立跑起来 |
| 3 | **换一台电脑 ⇒ 自动完成配置** | 拷目录 → 跑一次 `install.py` → 钩子全通,**⛔ 不手改绝对路径** |
| 4 | 所有会话遵循会话机制 + 多会话协作可独立使用 | 全局注入面生效;包内自包含,⛔ 不依赖旁件 |
---
## 二、现状(2026-10-01 实测 · 定向复核)
### 2.1 家当散在三处(这是要解决的第一性问题)
| 落点 | 装什么 | 实测规模 |
|---|---|---|
| `~/.workbuddy/skills/multi-session-collab/` | 协作本体 | **14 个文件**(其中 2 份是 `.bak-*`,⛔ 不属机制) |
| `D:/github/dsh_shenxian/dsh-server-docs/07-scripts/` | 宿主钩子 + 锁 | 目录内 **29 个脚本 / 配置文件**,其中**属机制 9 份** |
| `$WS/.workbuddy/` | 运行态 | 分两个子目录:`collab/`(协作程序与配置,**9 个文件** + 4 个子目录)+ `tools/`(监控 / 成本 / 会话查找等辅助,**23 个条目**) |
### 2.2 精确拷贝源清单(任务 1 直接照抄这张表)
**① 协作本体 → 包 `scripts/collab/`**
| 源(`~/.workbuddy/skills/multi-session-collab/`) | 说明 |
|---|---|
| `scripts/collabd.py` | 协作主程序(177 KB 级) |
| `scripts/board.py` | 看板 |
| `scripts/guard.py` | 守卫 |
| `scripts/selftest.py` | 自检 |
| `scripts/collabd.config.example.json` | 配置模板(`install.py` 的输入) |
| `references/{architecture,deploy,pitfalls,taskgraph}.md` | 4 份 references |
| `assets/{board.html,design-tokens.css}` | 2 份前端资产 |
| `SKILL.md` | 改造为包内两段式总入口(⛔ 不是原样拷) |
⛔ **不拷**:`scripts/collabd.py.bak-digest-20261001`、`scripts/collabd.py.bak-ledger-first-20261001`(历史备份,非机制)。
**② 宿主钩子 + 锁 → 包 `scripts/hooks/` 与 `scripts/lock/`**(源 `DOC/07-scripts/`)
| 源文件 | 归入 | 钩子事件(见 2.3) |
|---|---|---|
| `session-log-guard.py` | `scripts/hooks/` | PostToolUse + UserPromptSubmit |
| `lock-guard-hook.py` | `scripts/hooks/` | PreToolUse(`Write\|Edit`) + SessionStart |
| `bash-output-guard.py` | `scripts/hooks/` | PreToolUse(`Bash\|Read`) |
| `stop-dialog-guard.py` | `scripts/hooks/` | UserPromptSubmit |
| `skill-load-guard.py` | `scripts/hooks/` | UserPromptSubmit |
| `handoff-guard.sh` | `scripts/lock/` | 锁(手工调用,非钩子) |
| `preflight-lock.sh` | `scripts/lock/` | 开工前置(手工调用) |
| `handoff-status.py` | `scripts/lock/` | 锁状态(手工调用) |
| `op-lock.sh` | `scripts/lock/` | 操作锁(手工调用) |
**③ 运行态 → 包 `scripts/collab/` 与 `scripts/forensics/`**
| 源(`$WS/.workbuddy/`) | 归入 |
|---|---|
| `collab/goalctl.py` | `scripts/collab/` |
| `collab/board_ext.py` | `scripts/collab/` |
| `collab/wake-session.py` | `scripts/collab/` |
| `collab/stop-collab.py` | `scripts/collab/` |
| `collab/deliver-gateway-token.py` | `scripts/collab/`(超 §3.4 明列清单的**补充**,理由:属协作投递链、要求 4「可独立使用」) |
| `collab/collabd.config.json` | `scripts/collab/`,改名 `collabd.config.json.bak-live-20261001`(**本机活配置留存**,供 `install.py` 比对;⛔ 不作为分发模板) |
| `tools/wb-result-hook.py` | `scripts/hooks/` |
| `~/.workbuddy/skills/workbuddy-session-forensics/scripts/proc-parent.py` | `scripts/forensics/` |
### 2.3 宿主钩子接线现状(`settings.json` 实测 · **14 处** · 指向 3 个不同目录)
| 事件 | matcher | 指向 |
|---|---|---|
| PostToolUse | (空) | `07-scripts/session-log-guard.py`(`-S`) |
| PreToolUse | `^AskUserQuestion$` | **`ai1net-decision-laya/bridge/decision_bridge.py`** |
| PreToolUse | `^Bash$` | `$WS/.workbuddy/tools/wb-result-hook.py` |
| PreToolUse | `Write\|Edit` | `07-scripts/lock-guard-hook.py` |
| PreToolUse | `Bash\|Read` | `07-scripts/bash-output-guard.py` |
| SessionEnd | (空)×2 | **`decision_bridge.py`** / `wb-result-hook.py` |
| SessionStart | `startup\|resume` | `07-scripts/lock-guard-hook.py` |
| SessionStart | (空) | **`decision_bridge.py`** |
| UserPromptSubmit | (空)×5 | **`decision_bridge.py`** / `wb-result-hook.py` / `stop-dialog-guard.py` / `skill-load-guard.py` / `session-log-guard.py`(`-S`) |
🔴 两条硬事实:
1. **14 处全部硬编码 python 绝对路径**(`E:\ProgramData\.workbuddy\binaries\python\versions\3.13.12\python.exe`)⇒ **换机器必碎**,正是要求 3 要治的。
2. **`decision_bridge.py` 出现 4 处,但它属另一条线**(`ai1net-decision-laya`)⇒ ⛔ **不并入本包、⛔ 不改它的 4 条接线**。本包只接管其余 **10 处**。
### 2.4 引用面(搬动前 blast radius · 实测)
`handoff-guard` 被引用:文档库 5 / 工作区 2;`preflight-lock` 2/1;`op-lock` 3/0;`lock-guard-hook` 3/0;`session-log-guard` 3/0;`stop-dialog-guard` 6/0;`skill-load-guard` 4/0;`bash-output-guard` 5/1。
⇒ **合计约 37 处引用**(不含 `decision_bridge`)。**⛔ 不能 `mv` 走**:任一漏改 = 静默失效(`pitfalls` 明写「同名两份实现是最难查的故障」)。
### 2.5 🔴 任务 1 执行后回填(**以本节为最终实测值**)
⚠️ 起草本文件时曾出现一处**误判**(把 `collab/` 记为「空」)—— 起因是 `ls -1 目录A 目录B | sort` 把两个目录的输出**合并排序**了,误读成同一目录 ⇒ 已按分开列目录更正。**教训:多目录用一条 `ls` 会被合并排序,必须分开列。** 本节即为更正后的实测值。
| 项 | §3 原文 | 最终实测 | 处置 |
|---|---|---|---|
| `$WS/.workbuddy/collab/` | 列为一个落点 | **9 个文件**:`board_ext.py` `collabd.config.json` `deliver-gateway-token.py` `gateway-schedules.json` `goalctl.py` `stop-collab.py` `wake-pulse.sh` `wake-session.py` + 1 份 `goalctl.py.bak-*`;另有 4 个子目录 | 运行态源**取自此目录**,不是 `tools/` |
| `$WS/.workbuddy/tools/` | 列为一个落点 | **23 个条目**,全为**监控 / 成本 / 常驻**类(`cost_model.py` `keepalive.py` `thin-consumer.py` `wb-supervisor-watch.py` 等)+ `wb-result-hook.py` | 只取 `wb-result-hook.py`(钩子);其余**暂不纳入** |
| `multi-session-collab/scripts/` | 「14 文件」 | 14 中含 **2 份 `.bak-*`** | 实际机制 = 12 份,⛔ bak 不拷 |
| `references/{collab,rules,forensics}.md` | 列在目录表 | **无源,属本包新写** | 任务 4 写 |
| `manifest.md` | 列在目录表 | 任务 1 已生成 | ✅ 已完成 |
---
## 三、方案对比(≥2 项 · 利弊 · 建议)
### 方案 A · 单一实现源 + 原位转发壳 ✅ **建议采用**
包内 = **唯一实现**(`cp` 拷入);`07-scripts/<同名>` 与 `$WS/.workbuddy/tools/<同名>` **改成 3 行转发壳**(`exec` 包内那份),⛔ **不删原件、不动物流**。
- **优点**:① **零 blast radius** —— 既有 37 处引用**一个字都不用改**,全部继续可用;② 换机器只需搬**一个目录**(正是要求 3);③ **消灭「同名两份实现」**这个最难查的故障模式(读数为准的只有一份);④ **可灰度**:先拷壳 → 验壳 → 再切,每一步都能停;⑤ 回滚只需还原壳文件与 `settings.json` 备份。
- **缺点**:① 文档库排错时多一层间接(看 shell 才知道真身在哪);② 若用户日后**删掉整个包**,转发壳会指向不存在的目标 ⇒ 需在 `install.py --uninstall` 里**先还原壳再让用户删**;③ 需要一次「改壳」动作(9 个脚本),本轮**必须放在最后做**(有 blast radius)。
### 方案 B · 直接 `mv` 到包内 + 全量改引用 ❌ **淘汰**
- **优点**:结构最干净,读代码只有一处;无转发跳数。
- **缺点**:必须**一次性**改完约 37 处引用,任一漏改 ⇒ **静默失效**(无报错、只在关键时刻掉链子);无法灰度;回滚要把 37 处反向改回来。**风险与收益不匹配 ⇒ 不可接受。**
### 方案 C · 只做「配置器」包,机制脚本原地不动 ❌ **淘汰(不满足需求)**
- **优点**:改动最小、风险最低;`install.py` 只做钩子接线与工作区初始化。
- **缺点**:**直接违反要求 1、2、3** —— 没有「一个包」、没有整合、换机器仍要搬三处。**需求未达 ⇒ 淘汰。**
### 方案 D · 硬链接 / junction 把三处指到一个包 ❌ **淘汰**
- **优点**:零拷贝、单一实现、引用不用改。
- **缺点**:Windows 下 junction 需管理员或开发者模式,**跨盘符不支持硬链接**(本机 C: 与 E: 并存);**换机器必碎**(恰恰是要解决的问题);R11「交互 / 便利性」净变差。**淘汰。**
### 🔴 建议
**采用方案 A。** 它是唯一同时满足「零 blast radius」「换机器只搬一处」「消灭双实现」三项的候选;B 的干净结构以 37 处引用的一次性正确率为代价,不值得;C 不满足需求;D 与「换机器」目标直接冲突。
---
## 四、推荐方案的落地设计(任务 1–4 照此执行)
### 4.1 包内目录(照 §3.4 第 2 条 · 已定项)
```
~/.workbuddy/skills/session-mechanism/
SKILL.md # 总入口;两段可分别加载:「会话机制」/「多会话协作」
install.py # 🔴 自动配置(本包核心交付物)
references/
architecture.md deploy.md pitfalls.md taskgraph.md # ← 拷自 multi-session-collab
collab.md rules.md forensics.md manifest.md # ← 🆕 本包新写(任务 4)
scripts/
hooks/ session-log-guard.py lock-guard-hook.py bash-output-guard.py
stop-dialog-guard.py skill-load-guard.py wb-result-hook.py
lock/ handoff-guard.sh preflight-lock.sh handoff-status.py op-lock.sh
collab/ collabd.py board.py board_ext.py goalctl.py wake-session.py
guard.py selftest.py stop-collab.py deliver-gateway-token.py
collabd.config.example.json collabd.config.json.bak-live-20261001
forensics/ proc-parent.py
assets/ board.html design-tokens.css
```
✅ **任务 1 已完成**:上述 27 个文件已 `cp` 拷入,逐文件 `py_compile` / `Git-Bash bash -n` / `json.loads` 全通过(失败 **0**),并与源**逐字节 md5 一致**;清单见包内 `references/manifest.md`(含 provenance 与「暂未纳入」清单)。
⚠️ `references/` 中 `collab / rules / forensics / manifest` 四份**当前没有源**,属**新写**(任务 4);⛔ 不要去找现成文件。
### 4.2 `install.py` 五项职责(照 §3.4 第 5 条 · 已定项)
| # | 职责 | 关键约束 |
|---|---|---|
| ① | **自解析** | 解释器一律 `sys.executable`(⛔ 不硬编码 python 路径);配置目录按 `CODEBUDDY_CONFIG_DIR` 推导(⛔ 不硬编码盘符) |
| ② | **钩子接线** | **声明表驱动**(14 处 → 一张表,`decision_bridge` 的 4 条**排除在外**);**先删自己旧条目再插**(幂等);写前**备份**;支持 `--dry-run`;`--uninstall` **逐字节还原** |
| ③ | **工作区初始化** | `--workspace <path>` ⇒ 由 `collabd.config.example.json` 生成 `collabd.config.json`(路径按本机推导)+ 写工作区规则锚 |
| ④ | `--verify` | 每个钩子跑**空载荷**须 `rc=0` + `collabd.py --where` + `selftest.py`(PASS / FAIL 0) |
| ⑤ | **日志** | 全部写操作写 `install.log` |
### 4.3 「所有会话遵循」的注入面(照 §3.4 第 6 条)
- ✅ **已确认可用**:① 全局钩子(`settings.json` 全局生效)② 技能全局可见 ③ `~/.workbuddy/MEMORY.md`(用户级记忆,跨会话注入)。
- ⚠️ **未取证**:WorkBuddy 是否读 `~/.workbuddy/AGENTS.md`。
⇒ 🔴 **任务 2 开工前必须先只读取证**(线索:`app.asar.unpacked/cli/dist/codebuddy-headless.js`、`codebuddy-lite-wb.mjs` 里出现 `AGENTS.md` 字样)。**⛔ 不许猜、不许跳过。** 若读 ⇒ 本包多一个全局注入点(写在 `install.py` 里落一份锚文件);若不读 ⇒ 注入面收敛为上述三条,方案不动。
### 4.4 转发壳(照 §3.4 第 4 条 · 🔴 **放最后做**)
`DOC/07-scripts/<同名>` 与 `$WS/.workbuddy/tools/<同名>` 改为 3 行壳:定位包内真身 → `exec` 之。
- ⛔ **不删原件**:先把原件改名 `*.orig-<日期>` 还是先用 `.bak-forwarder-<日期>` 备份 ⇒ **必须备份,再覆盖**(`R7`)。
- ⛔ **不带 `--domains` 的独占锁期间才能做**(改的是全平台共用脚本 ⇒ 机制层)。
- 验收:`handoff-guard.sh --status` 行为与改前一致。
---
## 五、红线 R1–R11 逐条自查
| # | 是否命中 | 自查结论 |
|---|---|---|
| **R1** 不自动升级 dsh | ⛔ 不命中 | 本方案只动 `~/.workbuddy/skills/`、`settings.json`、文档库脚本与工作区 `tools/`,**不碰任何 dsh 升级链路** |
| **R2** 不改官方 dsh 主程序与缓存 | ⛔ 不命中 | ⛔ 全程不写 `@deepseek-ai/dsh` 及其缓存;扩展只走 profile 层既定机制 |
| **R3** client bundle 禁 `exports.default` | ⛔ 不命中 | 不改任何 client bundle |
| **R4** 不用真实账号测登录 | ⛔ 不命中 | 本轮无登录类验证 |
| **R5** 权限只准收窄 | ⛔ 不命中 ⇒ **附权限影响评估(见 5.1)** | 无新挂载 / 无放开遮蔽 / 无暴露平台目录或 env / 无放宽 nft / 无提档位;钩子**不新增事件类型**(已存在的 5 类) |
| **R6** 先查已有资产再动手 | ✅ 已履行 | 已读接续包 §3 盘点 + 本轮定向复核三处源目录(见 2.1 / 2.2 / 2.5);未装任何新东西 |
| **R7** 禁未经确认的批量写入 | ✅ 已履行 | 拷贝涉及 **27 个文件**(>10)⇒ 已在 **2.2 表列出完整前置清单**,包内 `manifest.md` 亦有逐文件 provenance;均为 `cp` 新增到**新建目录**,⛔ 无覆盖、⛔ 无 `cp -r` 覆盖、⛔ 无全库遍历、⛔ 无 `git add -A`;本包正是用户明确要求(原话「整合…为一个 skill 包」)⇒ 属本 lane 内(`R7-边界`) |
| **R8** 生产变更直接做 | ⛔ 不命中 | 本方案**全在本机**,不触 `47.77.182.89` |
| **R9** 禁人工删锁 / 接管 | ✅ 已履行 | 本会话已 `--claim-exec` 抢到**全局独占锁**;⛔ 不删 `交接单/.exec-lock`、⛔ 不接管、⛔ 不以「疑似已死」为由删锁;**完工反序释放**;抢不到即停手(本轮已抢到) |
| **R10** 禁 root / 禁非该实例 uid | ⛔ 不命中 | 全程当前 Windows 用户,无实例、无 root、无 profile |
| **R11** 只做正向迭代 | ✅ 已自查 | 见 5.2,**十维无净变差** |
### 5.1 R5 · 权限影响评估(按 §1.5 B 阶段 2 附)
| 维度 | 结论 |
|---|---|
| 新增挂载 / 放开遮蔽 | **无**。包落在 `~/.workbuddy/skills/`(用户级技能目录,已有大量同类技能),沿用既有可见性,⛔ 不新增目录层级 |
| 暴露平台目录或 env | **无**。`install.py` 只读自身所在路径与 `CODEBUDDY_CONFIG_DIR`,⛔ 不外传、⛔ 不写平台 env |
| 放宽 nft / 提档位 | **无** |
| 钩子能力变化 | **不扩大**。14 处接线的事件类型(PostToolUse / PreToolUse / SessionStart / SessionEnd / UserPromptSubmit)**全部是已有的**;本包只做**指向改写**(绝对路径 → 包内路径),⛔ 不新增 `matcher`、⛔ 不扩大拦截面 |
| 写入面 | 唯一新增写动作 = `settings.json`(**写前备份**);其余均为用户级技能目录内新增文件,等价于「装一个技能」 |
| **净结论** | **权限不扩大** ⇒ 无需权限类确认;本节为履行流程留痕 |
### 5.2 R11 · 十维自查
| 维度 | 判断 | 说明 |
|---|---|---|
| 目标 | ↑ | 「一个包 + 自动配置」直接达成 |
| 方向 | ↑ | 与用户原话完全一致 |
| 架构 | ↑ | 三处合一;消除「同名两份实现」这一**已实测到过的**故障模式 |
| 功能 | → | 全为 `cp`,行为等价;`decision_bridge` 那 4 条**原样不动** |
| 性能 | → | 转发壳会多一次**进程启动**(脚本本身是短进程)⇒ 量级可忽略;⚠️ 本轮**未实测**耗时系数,⛔ 不报具体毫秒数 |
| 安全 | → | 权限不扩大(见 5.1) |
| 交互 | → | 对使用者无感知变化 |
| UI | → | 不涉及 |
| 便利性 | ↑↑ | 换机器:从「搬三处 + 手改 14 处绝对路径」→「跑一次 `install.py`」 |
| 扩展性 | ↑ | 新增机制脚本从此有**唯一落点**,不再三处扩散 |
| **结论** | **无净变差** | 唯一潜在劣化 = 转发壳一跳(可忽略)+「包被删后壳悬空」(🈯 用约束堵住:`--uninstall` **先还原壳**,再让用户删包) |
---
## 六、执行顺序与验收
**顺序(一次只做一件事,做完即停)**
1. **任务 1 · 建骨架** —— 建 `~/.workbuddy/skills/session-mechanism/`;按 **2.2 表** `cp` 拷入(⛔ **不 `mv`**);逐个 `py_compile` / `bash -n`;算 md5 记进 `references/manifest.md`。
2. **任务 2 · 写 `install.py`** —— 🔴 **开工前先只读取证 §4.3 的未取证项**;然后按 4.2 五项职责实现。
3. **任务 3 · 本机实测** —— 见下验收判据,⛔ **不许只测 happy path**。
4. **任务 4 · 文档与收口** —— `SKILL.md`(两段式)+ `references/deploy.md`(换机器)+ **最后才做 4.4 转发壳** + 三层沉淀 + 反序释放锁。
**验收判据(照接续包 §5,逐条打钩)**
| # | 判据 | 状态 |
|---|---|---|
| 1 | `install.py --dry-run` diff 与预期一致;真装后**再跑一遍 ⇒ 零改动**(幂等) | ⬜ 待任务 3 |
| 2 | 装机后 `--verify`:钩子空载荷全 `rc=0`;`collabd.py --where` 正确;`selftest.py` **PASS / FAIL 0** | ⬜ 待任务 3 |
| 3 | `--uninstall` 后 `settings.json` 与装前备份**逐字节相同**(`cmp`) | ⬜ 待任务 3 |
| 4 | 包拷到**另一个目录**再跑 `install.py` ⇒ 钩子指向新路径且 `--verify` 全绿(=「换电脑」最小可复现) | ⬜ 待任务 3 |
| 5 | `07-scripts/` 转发壳:`handoff-guard.sh --status` 行为与原型一致 | ⬜ 待任务 4 |
---
## 七、风险与回滚
| 风险 | 触发条件 | 处置 |
|---|---|---|
| 改壳后既有引用失效 | 转发壳写错、路径推导错 | 改壳前**逐个备份** `*.bak-forwarder-20261001`;验收 5 必做;异常 ⇒ 立刻还原备份 |
| `settings.json` 被写坏 | `install.py` 逻辑错 | 写前备份 `settings.json.bak-<用途>-20261001`(沿用本目录 6 份先例的命名);`--uninstall` 须 `cmp` 逐字节相同 |
| 包被删后壳悬空 | 用户手工删包 | `--uninstall` **先还原壳、后允许删**;`install.log` 留痕 |
| 拷贝把历史备份当机制 | 误拷 `.bak-*` | 2.2 表已注明 ⛔ 不拷 `collabd.py.bak-*` |
| 动了 `decision_bridge` 那条线 | 表驱动误包含 | `install.py` 的声明表**显式排除** `ai1net-decision-laya` 的 4 条,并在 `--dry-run` 输出里**打印被排除项**以便目视核对 |
**回滚三件**:① 还原 `settings.json` 备份 ② 还原 9 个转发壳备份 ③ 删除包目录。三步互不依赖,可单独回。
---
## 八、留给后棒的两件事
1. **§3.4 第 3 条(并入范围)可一句话推翻** —— 现值 = 并入 `multi-session-collab`(机制本体)+ `workbuddy-session-forensics`(2 个文件:`SKILL.md` + `scripts/proc-parent.py`);**不并** `agent-operating-rules`(跨项目「作业总规矩 / 说话方式」层,被大量模板引用,只在包内 SKILL.md **声明依赖**)。推翻成本低(多拷 4 个 references)。本轮**按现值执行**。
2. **文档库 `04-调整方案/` 占号建档未做** —— 本轮按用户指定落点写在 `$WS/交付物/`。若要求并入文档库正式方案序列,可在**任务 4 收尾**时补一次原子占号(届时仍须持锁)。
---
## 附 · 本轮改动边界(**零改动既有机制文件**)
**新增(3 处)**
1. 本文件(`$WS/交付物/会话机制合并技能包-方案-20261001.md`)
2. 新包 `~/.workbuddy/skills/session-mechanism/`(27 个文件,**全部 `cp`**,含新生成的 `references/manifest.md`)
3. `$WS/tmp/inv-20261001/build-manifest.py`(一次性脚本,⛔ 不入库)+ 今日日志一段
**⛔ 零改动**:`settings.json` / `DOC/07-scripts/` 下任何文件 / `$WS/.workbuddy/` 下任何文件 / `multi-session-collab` 下任何文件 / `workbuddy-session-forensics` 下任何文件 / 自动化排期。
⇒ **旧路径全部原样可用**,`decision_bridge` 那条线未触碰 ⇒ **无回滚负担**(回滚 = 删掉新包目录即可)。
**本机踩坑记录(供后棒省时)**
- `ls -1 目录A 目录B | sort` 会把两目录输出**合并排序** ⇒ 误判 `collab/` 为空。多目录必须**分开列**。
- Python `subprocess.run(["bash", …])` 在本机落到 **WSL 启动器** ⇒ `bash -n` 假阴性(乱码 + 路径被吃)。必须显式用 `E:/ProgramData/.workbuddy/binaries/PortableGit/versions/1.2.0/usr/bin/bash.exe`。