Skip to content

feat: 多 bot 协作派活 + 交付验收(任务进账本、产物要核验、dashboard 看进度)#278

Draft
sensuossss wants to merge 105 commits into
masterfrom
feat/verified-delivery
Draft

feat: 多 bot 协作派活 + 交付验收(任务进账本、产物要核验、dashboard 看进度)#278
sensuossss wants to merge 105 commits into
masterfrom
feat/verified-delivery

Conversation

@sensuossss

@sensuossss sensuossss commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

这个 PR 解决什么

以前一个 bot 把活派给另一个 bot,到底做没做、做得对不对,全靠群里一句「做完了」——没法核实。这个 PR 加了一套可验收交付机制:

  • 派出去的每个任务都进账本botmux dispatch),不是聊天记录。
  • 干活的 bot 必须交产物/证据botmux report),不能只在群里口头说完成。
  • 派活方核验通过才算交付botmux delivery accept/reject):账本是唯一真相,accept 必须留下「核验了什么、跑了什么命令」的硬证据。
  • 支持两层协作:主群里你跟规划 bot 对话,它开一个「目标群」拉一个监管 bot,监管 bot 再把子任务派给干活 bot,并负责核验、催办、汇总——你只在主群看里程碑。
  • dashboard 加了「目标」页:一眼看到每个目标下所有子任务的状态/验收情况。

具体能力

任务账本(核心)

  • 极简 append-only 账本(4 事件 + 幂等 + 物化读模型);worker 不能群里口头说完成,必须带证据 report,由派活方(LLM 充当审核)accept/reject。

两层协作编排

  • 目标群 = 独立群、子任务 = 群级一次派活(用账本 taskId 隔离、不开话题);目标的「章程/目标/状态」存在独立 charter(不污染会话)。

自动兜底

  • 干活 bot 没按规矩交报告时,后台 watchdog(worker 空闲即时 + 5 分钟定时)唤起监管 bot 主动核验产物,不会卡死。
  • 监管 bot 完成后用 daemon 内部通道唤起主群规划 bot(绕过同 bot 自己发消息收不到的限制)。

结构化验收 + 看板

  • 验收标准从自由文本升级成结构化字段(要核验哪些产物文件、跑哪些命令),派活时即时校验非法标准;watchdog 把它渲染成人类可读的清单。
  • dashboard 目标 页 + GET /api/goals(默认 token 门禁,不公开)。

验证

  • pnpm build 绿;pnpm test(unit)5001 passed,仅 3 个 workflow-fanout 因本机 inotify 实例耗尽报 EMFILE——环境问题、非代码,且本分支没碰 workflows 逻辑。
  • 部署到线上后 3 项实跑验证全绿:派非法验收标准会被拒且不落账、目标看板接口鉴权正确且能读真账本、watchdog 渲染清单不再贴原始 JSON。
  • 历轮多 bot 两层 live e2e 全部通过。

同步与冲突

已 merge 最新 master(merge dfebc620),唯一冲突是 dashboard.ts 相邻 import,两侧都保留。现 0 behind / 35 ahead。

Review 分工

  • claude:账本 types/校验/读模型 + 协议 skill + dashboard 前端
  • codex:cli / core(watchdog, dashboard-ipc-server, worker-pool) / 主 dashboard server

- L2-2: acceptanceHint JSON v1 给 3 个可复制模板(单文件/命令/组合)+ dispatch 前 python json.load 自检,防手写 JSON 漏引号/尾逗号导致巡检退化成只能催
- 可信交付: 验收留痕升级为硬规矩——accept 必带 --evidence-checked、能跑命令必带 --ran-command,禁止空证据/看一眼算过;兜底通道(worker 没 report)更要逐条对 acceptanceHint checks 写明结果
…native 唤起)

- L2-5: send --chat-id 主群 → goal notify-parent(L1/L2 同 bot,send 是 self-message 被 guard 挡;notify-parent daemon-native 绕开),补 --summary-file/自动推断 session 说明
- L1-5: 描述改 notify-parent 唤起([goal-parent-notify] 前缀),强调通知只是唤醒、L1 仍须查账本/charter
- L1-4: goal supervise 加 --session-id 建议(填 prompt 顶 <session_id>),让 L2 完成时不论 L1 是话题/群级会话都能精准回定位
- 去掉 watchdog 巡检的 --older-than 5m:daemon 唤醒判据是'任何 dispatched/rejected',skill 却按 5min 年龄过滤→唤起卡 5min 边界时把刚到点任务漏掉、白唤;改为处理 [goal-watchdog] 消息已列出的 pending task(--older-than 只留给 L2-4 主动扫长期卡住)
- 兜底 accept 改 report-then-accept:cli delivery accept 必须挂在一笔 report 上(cli.ts:4696 无 reportId 报错),'worker 没 report 也认'原文直接 accept 执行不了;改为 L2 先补一笔自证 report(把核验当证据)再 accept --report <id>。这是 P0 时 L2 实际做法、被掩盖未写进协议
配合 codex e3e20d8 的 goal-chat-store + evaluateTalk 门控:在 L1-4
goal supervise 段点明——goal 群登记为授权制,只有群主+编排自家 bot(peer)
能和 bot 对话,第三方 @bot 被拦(弹授权卡需 grant);普通 /g 群不受影响。
# Conflicts:
#	src/core/session-manager.ts
#	src/dashboard.ts
#	src/dashboard/web/style.css
配合 codex 1e75a72/92d6e1c8 的 goal supervisor 自动复活 + worker health fact 注入:
- L1-4 加「L2 监工会自愈」: L2 崩/断后 daemon 按 registry 自动重起,连崩超
  预算转升级给人,复活后重建状态续巡检
- L2-3.5 加「收到 [worker-health] 注入」处理: 按 session/workerProcess 真实
  状态判断——真死重派/休眠唤醒/busy 等待,别机械催死 worker
…ds(B2 确定性重派元数据)

为 B2 worker 确定性重派铺路:TaskDispatchedPayload + TaskView 加
workerLarkAppIds?/workerCliIds?(与 workerOpenIds/workerNames 同序),
materialize 透传。sessionId 不入事件(运行态,watchdog 按 goalChatId+
larkAppId/openId 解析)。codex 负责 dispatch 时 populate + watchdog 重派消费。
watchdog 确定性重派后向 goal 群发 🔄 卡(原 worker 掉线、任务已重派),
复用 TaskDispatched 做 ledger 留痕、观测性靠这张 narration 卡。
事件:{type:'reassigned',key,taskId,deadWorker?}。narration 2/2 绿。
配合 codex f9b24a5:worker 死透(daemon 在线、会话缺失/killed)系统自动同
bot 重派+🔄卡,L2 别重复只接管监督;daemon 离线/休眠/反复重派 才需 L2
判断(等/缩scope/escalate)。整合进 L2-3.5 worker 掉线兜底段。
TaskDispatchedPayload/TaskView 加 workerBotUnionIds(与 workerOpenIds/Names/
LarkAppIds/CliIds 同序),ledger materialize 一路带过。union_id 是唯一既跨 app
稳定、又出现在每条入站事件(sender_id.union_id)的 worker 标识,作为跨设备/外部
交付信封的授权锚:远端 worker 发 report/help 时按 senderUnionId∈workerBotUnionIds
授权,无需预热观测。不替代 workerLarkAppIds/workerCliIds(服务本机重派/健康)。

codex 接 daemon ingestion union_id-first/open_id fallback + dispatch 写该字段。
tsc 干净;verified-delivery 76/76。
新增 services/bot-union-ids-store(按 botName 存 name→union_id,大小写不敏感)。
event-dispatcher 在 bot-sender 分支用 sender_id.union_id + open_id cross-ref 反解
name,记 name→union_id(name 解不出时打诊断日志、不记)。federation-spoke-api
localBots 上报 botUnionId(从 store 按 name 取)→ 经 sync 进 hub 的联邦花名册。

这给跨设备授权链补上唯一缺失的源:bot 自身 union_id 此前无处可取(bots-info 只有
botOpenId、observed-bots-store 只存 openId+name、/bot/v3/info 拿不到)。L2 dispatch
可据此填 workerBotUnionIds(codex 域),ingestion 按 union_id 授权远端 worker 信封。

live probe 已实证 bot 事件带非空 on_ 格式 union_id。tsc 干净;store 7/7 + 相关 23/23。
@deepcoldy deepcoldy marked this pull request as draft June 26, 2026 05:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant