Files
workbuddy_skills/session-mechanism/references/deploy.md
T
admin 19101acd65 init: workbuddy_skills 重建,仅收录 session-mechanism
- 按用户指示清空原有 25 技能内容,只提交 session-mechanism(57 文件)
- 附 .gitignore(产物 + 本机凭据)
- 令牌明文已脱敏(历史 .neodata_token 与 pitfalls 引用均不入库)
- 本提交为孤儿提交(父提交为空),历史自此重新开始
2026-10-05 14:13:24 +08:00

16 KiB
Raw Blame History

换一台机器怎么用(部署手册)

🔴 当前结论(先读这里 · 最后更新 2026-09-30 05:55)

项 当前结论
本机起法 唯一可行 = 宿主后台任务机制 + stdout 全重定向到文件(完全静默)。⛔ detached 活不过工具调用边界;⛔ schtasks 被内置程序黑名单硬拦。
投递 🔴 定案:协作与投递「一直运行」(常驻) —— 09-29 定案,2026-10-01 用户再确认(原话「协作与投递一直运行(常驻)」;理由「可能不是所有队列都是钩子产生的」)。⚠️ 现状:守护处于停机(2026-09-29 23:59 止损;其归因已于 09-30 审计修正 —— 真因是日志撞 ~10 MiB 被丢写,⛔ 不是"挂在会话名下")⇒ 拉起常驻见 §5。⛔ 「用自动任务当闹钟」2026-10-01 已废弃。
前置(覆盖网络节点中继客户端 + 设备接入本地反代) 会周期性掉(实测:设备接入本地反代约 1h 被 reg.exe 拦死)⇒ 探测与自起见 §5b;⚠️ AI 侧无法自动重起,只能报警 + 人工/会话拉起。 ⚠️ 两段都在线 ≠ 链路通过(判据=中继真转发过流 streams>0)。
三条死路(都实测过,⛔ 别再试) detached spawn · schtasks · 从 bash 调 powershell.exe
⚠️ 本文件为追加式 ⇒ 与本节冲突时以本节 + architecture.md 的「当前结论」节为准。

目标:整套「多会话协作机制」只在这一个 skill 里(代码 + 架构 + 规范 + 手册),换机器=拷这个目录 + 改一份配置 + 接一次钩子。 ⛔ 换机器不需要拷贝任何工作区里的脚本 —— 若发现机制代码出现在工作区,那就是旧副本,按 §4 退役。


0 这个 skill 里有什么(机制本体)

文件 是什么
SKILL.md 操作入口:怎么派活、怎么收尾、怎么排坑
references/architecture.md 🔴 唯一架构文档(五主体/需求台账四态/单条握手/三条件唤醒/红线/判据/术语)
references/deploy.md 本文件(换机器)
references/pitfalls.md 踩坑清单(每条都真实发生过)
references/taskgraph.md 任务图规范
scripts/collabd.py 常驻程序(--once 投影轮)+ 投递(--tick 投递轮)+ 🔴 常驻形态(--supervise,定案=一直运行,见 §5)+ 上报 / 台账 / 看板
scripts/guard.py 🟡 可选:守护(只做发现+落盘,⛔ 不承担投递)。⛔ 不是投递的前置
scripts/selftest.py 回归自测(20 条用例 · 不碰生产):改完必跑,全绿才算改完
scripts/collabd.config.example.json 配置模板(拷成 collabd.config.json 改路径)

⛔ 不进 skill 的:运行态产物(台账/看板/通知/日志)—— 它们属于每个工作区自己,路径由配置的 workspace + inbox 决定。


1 前置(一次性)

项 要求
Python 3.11+(实测 3.13);ast.parse 可用的标准库即可,⛔ 无第三方依赖
宿主 WorkBuddy 桌面版(需要它的三张只读表 + 自动化排期 + 钩子)
目录 把本 skill 放到 <配置目录>/skills/session-mechanism/(如 E:/ProgramData/.workbuddy/skills/)

2 三分钟部署

⓪ 声明**任务目标**(🔴 没有目标 ⇒ guard **拒绝启动**,rc=3):
      <python> <skill>/scripts/guard.py --goal "<一句话任务目标>"
   也支持手写 <workspace>/<inbox>/goal.json(title 必需;可附 acceptance_doc / taskgraph / lines)
① 拷 skill 目录到新机器的 skills/ 下
② 在 scripts/ 下:cp collabd.config.example.json collabd.config.json
   改 4 处必填:workspace / inbox / live / taskgraph      (其余按需,见 §3)
③ 🔴 **拉起常驻(投递本体)**:`<python> <skill>/scripts/collabd.py --supervise` —— **它同时就是唤醒时钟**。
   ⚠️ 起法约束见 §5;⛔ 「用自动任务当闹钟」已于 2026-10-01 废弃。
④ 🔴 **接钩子(只作"即时性"补充,⛔ 不是主路径)**
   让宿主在**两个**事件上调用一个**本地脚本**(脚本内部再去调 collabd):
     事件 A:`UserPromptSubmit`                → <python> .../wb-result-hook.py
     事件 B:`PreToolUse`(matcher `^Bash$`)  → <python> .../wb-result-hook.py
   脚本内部做两件事(都**节流**、都**隐窗**):
     · `collabd.py --once`  ⇒ 常驻程序跑一轮**投影**(⛔ 不投递)
     · `collabd.py --tick`  ⇒ 投递跑一轮 ⇒ **投递 + 推进队列**
   🔴 **它的定位 = 补充**:钩子是**宿主起的子进程** ⇒ 自带网关口令 + ⛔ 不占会话 + 零 token ⇒ 有事件时**立刻**投一版;
     但 ⛔ **不能靠它兜底** —— 「需要投递的时刻必然伴随会话在动」**是错假设**(队列可能由**非钩子来源**产生,那时没有事件 ⇒ 漏)。
   ⚠️ **新加的钩子事件要宿主重启后才生效**(`UserPromptSubmit` 那条**立即生效**);
   ⚠️ 钩子**只指向 skill/工作区里那一份**脚本,⛔ 不许有第二份(见 §4)。
⑤ 🟡 **守护:可选**(⛔ 不是投递前置;**投递本体= ③ 的常驻**)。
     它的职责只剩**发现**:长时间没人动 ⇒ 落 `STALL.md`/`NEED-USER.md`。
     · ⛔ 别在会话里起(会被回收);要起 ⇒ 独立窗口或「启动」文件夹 ⇒ 但它**拿不到口令 ⇒ 投递不了**(设计使然)。

验收(逐条可测)

  1. <python> collabd.py --where ⇒ 打印出正确的 workspace / inbox / 任务图路径;
  2. <python> collabd.py --once ⇒ rc=0,inbox/ 下出现看板与机械摘要,无异常栈;
  3. 造一条上报:collabd.py --report T1 --state running --line <线> --by <会话名> ⇒ collabd.py --reqs 能看到 T1 执行中;
  4. <python> collabd.py --tick ⇒ rc=0,打印一行 tick: … deliver=…; 🔴 真投递的判据 ⛔ 不是 rc=0:看 wakeups.jsonl 有没有新增一行 ok:true; 打印 no-token ⇒ 说明这个进程不在宿主进程树内(手工跑属正常;由钩子唤起时出现 ⇒ 异常)。
  5. 让一个会话声明角色:collabd.py --declare --role main ⇒ 输出 已声明角色:<sid> = main;
  6. <python> selftest.py ⇒ PASS 39 / FAIL 0。
  7. 🔴 常驻投递在跑(定案项,见 §5):进程活着 + collabd.py --where 解析到正确 workspace; 判「真投递」⛔ 不看 rc,看 wakeups.jsonl 有没有新增 ok:true。

3 配置项(collabd.config.json)

键 必填 说明
workspace ✅ 你的工作区绝对路径(正斜杠);台账/看板/通知都相对于它
inbox ✅ 运行态目录(相对 workspace),建议 tmp/supervise-inbox
live / taskgraph ✅ 看板文件 / 任务图 JSON(相对 workspace)
lines 建议 线名 → 中文名
host_db 否 宿主库路径;空 ⇒ 用 CODEBUDDY_CONFIG_DIR 推导
shim_port / client_entry / client_runner 否 服务自愈用;不做自愈就留空
wake_enable / wake_min_gap / wake_text 否 唤醒投递(需要网关口令在环境里)

⚠️ 本机配置(collabd.config.json)不要提交到公共仓库 —— 它含本机绝对路径。


4 旧副本退役(⛔ 否则"代码在 A、钩子在 B")

症状:改了 skill 里的程序却没生效;或 advance.md 等产物由另一个副本写出(内容与你预期不同)。

① 工作区里若有 collabd.py / advance-watch.py / keepalive.py / thin-consumer.py
   ⇒ 全是旧副本(职责已被本 skill 吸收)⇒ 改名加 `.retired-<日期>`,⛔ 不删(留回滚)
② 确认钩子指向的是 **skill 里的** collabd.py(`--where` 自证路径)
③ 重启后看 `inbox/` 产物的 mtime 是否随会话事件刷新 ⇒ 是则接线正确

🔴 同名的两份实现是"最难查的故障"(实测踩过:两份同时跑,同一秒给出互相矛盾的读数)。


5 投递的边界(🔴 一句话:投递 = 一个常驻进程(它同时就是唤醒时钟);钩子只作补充,⛔ 不排期、⛔ 不用自动任务)

有口令(能投递) 需要容器会话 必须活着的进程数 跨重启自动生效 投递延迟 零 token
🔴 宿主后台任务(现定案的载体) ✅ ✅ 需 1 个 ≥1 ⛔ 需再拉起 ✅ 可调(轮询间隔) ✅
宿主钩子唤起的一次性进程(降为补充) ✅ ⛔ 不需要 0 ✅ ⛔ 依赖事件 ⇒ 会漏 ✅
会话外常驻(启动文件夹/独立窗口) ⛔ 否 ⛔ 不需要 ≥1 ✅ ✅ 可调 ✅

⇒ 🔴 载体 = 宿主后台任务(本机实测:detached 活不过工具调用边界、schtasks 被内置程序黑名单硬拦) ⇒ ✅ 唯一可行 = 宿主后台任务机制 + stdout 全重定向到文件(完全静默)。 ⚠️ 「必须活着的进程数 = 0」⛔ 不得再拿来否掉常驻(2026-09-30 踩过:丢掉时钟 ⇒ 用户点破"成摆设")。

三条硬约束:

  1. 🔴 必须完全静默 —— stdout 重定向到文件。⚠️ 真风险是输出/事件量把会话日志推过 ~10 MiB 上限 ⇒ 宿主 diagnostic-log:dropped ⇒ 界面不再显示(⇒ pitfalls.md P0-2); ⛔ 不是"任务挂在会话名下"(该归因已作废)。
  2. ⚠️ 别从会话/工具调用里"直接"起(detached spawn)—— 调用一结束就被回收(实测:唤醒停在调用结束那一秒); 要用宿主后台任务机制起,stdout 重定向到文件。
  3. ⛔ 计划任务(schtasks)在部分机器被安全策略硬拦 ⇒ 兜底走「启动」文件夹(⚠️ 但那拿不到口令 ⇒ 投递不了,只能做发现)。

⚠️ 口令边界的正确表述:由会话之外起的常驻拿不到网关口令 ⇒ 它只发现 + 落盘(STALL.md/NEED-USER.md/看板)—— 这不是故障,是设计:collabd.py 用 FROM_HOOK 分辨两种"没口令"(钩子唤起却没口令 ⇒ 异常 ⇒ 落 NEED-USER.md;常驻没口令 ⇒ 只记日志)。


5b 🔴 「覆盖网络节点 · 中继客户端 + 设备接入 · 本地反代」的探测与自起(2026-09-30 实测定型 · 任何棒都要会)

背景:手机接入链路的最前一格是"设备接入本地反代 + 覆盖网络节点中继客户端 都在线"。它不在 ⇒ relay 无通道 ⇒ 入口④闸 503 device-unreachable ⇒ 整条链断。 ⚠️ 这两个进程不跨 WorkBuddy 重启,且本机无法做成计划任务(schtasks 被内置程序黑名单硬拦)。

① 先探测(⛔ 在跑就别重复起 —— 两份会抢同一 host 注册/同一端口)

netstat -ano | grep ":20090"                       # 设备接入 · 本地反代在听否
tail -1 E:/dsh-worker-dev/logs/overlay-bg-*.out.log  # 最近一行 state=up(for …) ⇒ 覆盖网络节点中继客户端在跑
curl -s --noproxy '*' -o /dev/null -w "%{http_code}\n" http://127.0.0.1:20090/   # 期望 200

② 不在才起 —— 🔴 必须"宿主后台任务机制 + stdout 全重定向到文件"(完全静默)

件 命令
覆盖网络节点 · 中继客户端 node D:/github/dsh_shenxian/lib/net/relay/main.js --client --url wss://ai1net.com/dshs-relay --host <hostId> --network <network> --keys-file E:/dsh-worker-dev/overlay/relay-keys.local.json --ports 20090(env:DSHS_OVERLAY_NODE_KEY_FILE / DSHS_OVERLAY_NODE_GRANT_FILE;<hostId>/<network> 从 E:/dsh-worker-dev/overlay/overlay-node.local.json 读)
设备接入 · 本地反代 node E:/github/dsh-desktop-0.1.7rc2/node_modules/tsx/dist/cli.mjs E:/ProgramData/AIProject/ai1net-dsh-desktop/.workbuddy/_devkit/launch-desktop-dev-017.mts(cwd=E:/github/dsh-desktop-0.1.7rc2;⚠️ 必须先 unset ELECTRON_RUN_AS_NODE,否则 Electron 退化成纯 Node)

成功判据(两条都要):覆盖网络节点中继客户端 ⇒ 日志出现本轮新增的 registered host=… accepted=[20090] 且 state=up;设备接入本地反代 ⇒ 20090 LISTENING + curl 200。

⛔ 三条死路,别再试(都实测过)

  1. detached spawn —— 活不过工具调用边界(日志 0 字节即死);wb-overlay-node-launch.mjs --detached 注释里"本机实测不可用"是真的。
  2. schtasks —— 内置程序黑名单硬拦(见 pitfalls 里那六项),⛔ 命令内不可放行。
  3. 从 bash 调 powershell.exe —— 被策略拦("绕过 PowerShell 安全检查");而 overlay-node-daemon.ps1 -Action run 是 powershell 前台长跑,也活不下来。

⚠️ 遗留单点(如实登记):这两条后台任务挂在"起它的那个会话"名下 ⇒ 起它的会话被回收 / WorkBuddy 退出 ⇒ 链路断。 ⇒ 起它们的会话在那段时间内不要关;断了就按上面"② 不在才起"重起一次。

6 换机器后必查的 5 件事

  1. --where 的三个路径是否都对;
  2. 钩子接线是否指向 skill(§4);
  3. 宿主库能否只读打开(sessions / automation_runs / automations 三张表读得到);
  4. 有没有别的实现在抢同一个 inbox(§4);
  5. guard.py 是否真的起来了(--status)+ 它是否会随开机自启。

5d 🔴 「会话整体停止」后怎么恢复(2026-09-30 立 · 用户追问逼出来的)

用户原话:「所以现在的问题就是 会话整体停止了怎么办 的问题」。

先分清三档 —— 只有第三档才需要动手:

档 场景 会怎样 怎么办
① 只有某个会话停了(WorkBuddy 还开着) 机制照跑 —— 钩子是全局注册的,任何会话跑 Bash/发消息都会唤起它 不用办
② 所有会话都 idle(WorkBuddy 还开着) 钩子不被唤起 ⇒ 不投递 不用办 —— 那时没人在看;一有动作,积压立刻补投
③ WorkBuddy 退出 / 机器休眠 整条协作链停摆 —— 唯一能"开新会话"的通道就是宿主自动化 按下面清单恢复

恢复清单(重开 WorkBuddy 后按序跑)

  1. 看前置两段:netstat -ano | grep ":20090"(判据只认 LISTENING 行,⛔ 别用 curl/connect —— 本机 Proxifier 会代理回环,返回是假的)。 缺了 ⇒ 按 §5b 拉。⚠️ 中继客户端那半边已计划任务化,通常自己会回来。
  2. 查过期未跑的一次性排期(status='ACTIVE' and schedule_type='once' 且 next_run_at < 现在): 有 ⇒ 新建一条(⛔ 改时间不会触发)。
  3. 跑一轮投影 collabd.py --once,让队列/看板/NEXT.md 刷新。
  4. 看 NEED-USER.md:有没有等你拍板的事。
  5. 看 blocked.json:受阻件是否已随状态更新解除(⛔ 别让它一直当队首、每次唤醒白跑)。

🔴 为什么"不需要防"(实测结论,⛔ 别再想做常驻去扛)

  • 实测(2026-09-30 08:2x):11 条一次性排期里 9 条准点跑;唯一 0 次那条的排期在创建时就已过期 —— ⇒ 宿主在跑,排期就准点;宿主不在,排期不会自己跑(调度器就是宿主本身)。
  • 能扛过 WorkBuddy 退出的:只有不需要口令的长跑 —— 实测就是覆盖网络节点中继客户端(已由计划任务持有, 父链 powershell ← svchost ← services ← wininit,⛔ 无 bash/无 WorkBuddy)。
  • 扛不过的:任何要投递的东西 —— 投递要网关口令,而口令的唯一合法来源是 WorkBuddy 进程 env (08:06:31 实测:计划任务上下文 envPresent=false / envLen=0)⇒ 「不占会话」与「有口令」二选一。
  • ⇒ 结论:「会话整体停止」不需要"防",只需要"恢复流程"。 ⛔ 不要再设计"常驻监督进程"去扛 —— 那条路已被实测堵死(要么没口令、要么拖住会话)。