Skip to content

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

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

feat: 多 bot 协作派活 + 交付验收(任务进账本、产物要核验、dashboard 看进度)#278
sensuossss wants to merge 135 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 实际做法、被掩盖未写进协议
# Conflicts:
#	src/bot-registry.ts
#	src/cli.ts
#	src/core/session-manager.ts
#	src/daemon.ts
#	src/im/lark/card-handler.ts
#	src/services/group-creator.ts
#	src/skills/definitions.ts
master 6358d0c(拉群分批每批5)把 group-creator 改用 addBotToChat
增量加 bot(createChat 改传 botIds:[]),但漏更新 test mock——8 个用例
在纯 master 上即失败。补 groups-store mock 的 addBotToChat(默认每 bot
加成功)、并把「creator 过滤」断言从 createChat.botIds 改到 addBotToChat。
老滕拍板命名走 B、与 codex 讨论定案:goal 不改名,a2a 专指跨设备
agent-to-agent 交付层。本提交先做低 churn 文档正名(内部模块/CLI 不动):
- L2-2.5 改名为「a2a」概念块,按 codex 护栏写成「goal 可选用的跨设备
  交付协议」而非新编排模式,本机/人工/远端 worker 并存
- 补上已建的 union_id 授权(workerBotUnionIds,open_id 兜底)+ 冷启动
  (远端 bot 先 @ 你一次学 union_id)+ 派活向需对方 /grant;回报向免 grant
- 信封必须原始文本(非渲染卡片的 botmux send)
- 「## 可信交付」加一行 a2a 指针(外部 worker 入同一本账)
新增 src/verified-delivery/attention.ts(纯模块,零 live 依赖):
- classifyTaskDisposition(task, ctx?):单 task 纯规则,看板与 watchdog 共用的唯一分类真相,
  输出 {bucket, reason(稳定code), next(描述非预测)},7 桶 needsHuman|blocked|inProgress|
  readyToVerify|completed|systemRisk|quiet。completed 仅 accepted;rejected→inProgress(重报中)。
  store/ledger 派生风险(重派预算耗尽/死信)经 DispositionContext 由调用方注入——纯函数不读时钟、
  不读 live session、不跑命令,避免 verified-delivery→core 层级倒置。
- buildGoalAttentionBoard(opts):在 buildGoalBoard 之上跨 goal 卷起注意力首屏
  (needsHuman/blocked/systemRisk 扁平 + 进行中/待验收/最近完成),保留 per-goal 下钻。

live 健康(supervisor/worker 存活)与 DispositionContext 数据装配留给 codex 的 IPC/watchdog 层。
+18 单测;tsc --noEmit / build / full unit suite 7014 全绿。
src/dashboard/web/goals.ts + style.css(纯前端,消费 GET /api/goals/attention,不探活):
- 目标看板顶部加跨 goal 注意力带:需要你拍板 / 卡住·风险 / 待验收(折叠 进行中·最近完成)。
  needsHuman/blocked/systemRisk 扁平卷起,每行 goal+worker+disposition.next(一句话)+age+recent-evidence。
- 点任一行 → 选中该 goal+task,复用现有 rail/grid/detail 下钻;拍板/求助行落到现成「下发决策框」,零新交互。
- systemRisk 按 source 区分来源:source:'live' → 🔴实时(脉冲,标"现场探测可能瞬时"),source:'ledger' → 📒账本(稳态)。
- 浏览器只拉 /api/goals/attention(含 perGoal 喂下钻),去掉 /api/goals 拉取;retries 面板与决策框不动。

接 codex 5a1a097 的后端/API(core/goal-attention.ts + IPC /api/goals/attention)。
tsc --noEmit ✓ / pnpm build ✓ / 全量 unit 7015 全绿 / git diff --check ✓。
浏览器渲染可视 smoke 待与 codex 联调部署后做。
肉眼 smoke 前的数据校验发现:live 账本里有一条 escalated 探针 task(goal "union e2e",
带 workerBotUnionIds,系 union-id e2e fixture)materialize 时无 taskId,序列化时该字段被省略。
attnRow/taskRow 里的 escapeHtml(t.taskId) / shortId(t.taskId) 对 undefined 会抛 TypeError
→ 注意力带整段渲染失败(load 兜底显示"加载失败"),且选中该 goal 时现有任务网格也崩。

- AttnTask.taskId 改可选;band 的 data-task 用 escapeHtml(t.taskId ?? '')。
- 现有 taskRow 的 shortId(t.taskId) / escapeHtml(t.taskId) 同样加 || '' 守卫(同根因、同文件)。
- 行为验证:对真实 GET /api/goals/attention 数据(23 条 band 行 + 33 个 grid task,含缺 taskId 那条)
  模拟全部 escapeHtml 调用,0 抛错(修复前必抛)。

根因——畸形探针 task 污染 live 账本——另行 flag 给 codex 清理/确认。
tsc --noEmit ✓ / pnpm build ✓ / git diff --check ✓。
frontend-design 方法论,回应"大胆、打破常规"。两笔重彩、其余克制:
1. 冷暖语义色,颜色编码"归谁负责":暖(信号橙)=需要你拍板/升级/决策,冷(青)=机器已核验/自动对账,
   琥珀=等监管,红=告警。新增 .goalboard 作用域 --gb-human/--gb-machine(light+dark 两套),
   背景走 color-mix 派生、随主题自适应。
2. 验收印(签名元素):被验收的交付在详情面板盖一枚印章——核验人 + 核验几项证据 + 跑几条命令,
   把反作弊证据链从埋着的 badge 提升为视觉主角;provenance badge / accepted pill / done LED 同步冷色印章化。
3. 待批命令带:注意力带做成暖色重栏(全页最重视觉),needsHuman 最强暖;
   systemRisk 保持红色告警、blocked 保持琥珀,与"拍板橙"区分开。
4. 台账排版:计数/时钟/ID 等宽数字对齐(tnum),eyebrow/表头加字距。

纯前端:style.css 为主 + goals.ts 加 sealHtml;复用全部现有 gb-*/attn-* 类与 DOM,
数据契约 / 点击下钻 / 决策框 / 重试面板零改动;不加外部字体(系统字 + 字重/字距/数字做个性)。
tsc --noEmit ✓ / pnpm build ✓ / CSS 括号平衡 2296/2296 ✓。像素由老滕部署后肉眼定。
老滕反馈需要你处理模块违和/无内边距/圆角夸张,重做注意力带:
- 行本是 <button>,被全局 button{border-radius:999px} 描成悬空弧线 → 显式 border-radius:0 改直边强调栏
- 卡片/分组/行补 16px 内边距;重做成与看板其它面板一致的干净卡片(surface+1px边+圆角)
- 暖色只留信号点+头部微染+左侧暖色强调栏,克制但有你的待办区信号

顺修一处潜伏 bug(30bf6fc 起):注释里 'gb-*/attn-*' 的 */ 提前闭合 CSS 注释,
导致紧随的 .goalboard{--gb-human/--gb-machine...} 整块自定义属性在 light 主题被丢弃
(dark 有 :root[data-theme=dark] 覆盖故未受影响) → light 下暖/冷色全失效(验收印青/升级橙 pill 同受影响)。

纯 style.css;tsc/build/CSS括号/注释扫描全绿;light+dark computed 变量与截图均验证通过。
# Conflicts:
#	src/core/dashboard-ipc-server.ts
#	src/dashboard/web/app.ts
#	src/im/lark/event-dispatcher.ts
#	test/dashboard-ipc.test.ts
#	test/group-creator.test.ts
Resolve platform team/bind integration conflicts with verified-delivery.

Keep the master self bot union-id store keyed by larkAppId and move the verified-delivery observed bot union-id registry to observed-bot-union-ids-store.ts.

Preserve team union-id trust and bot orchestration quota bypass in daemon quota enforcement; update message quota tests for the merged signature.

Verification: pnpm -s vitest run test/message-quota-enforcement.test.ts test/bot-union-ids-store.test.ts test/observed-bot-union-ids-store.test.ts; pnpm -s tsc --noEmit; pnpm -s build. Full pnpm -s test still has 5 upstream failing files reproduced on pure origin/master.
按产品化 P0 词汇表定稿(主控/监管者/执行者/提交结果/交付记录/验收通过/驳回/求助/升级给人/换执行者/目标说明/自动核验):

- ORCHESTRATE_SKILL 新增「对人说话用产品词」指引段:skill 手册保留内部词保证 bot
  操作精确,但 bot 发给人的一切内容(对齐提案/升级理由/汇总/播报补充)一律翻译成
  产品词——治理双读者问题,不是把操作手册改成人话。
- goals.ts 显示层「自动对账」label/kind →「自动核验」(eb4aebc6 已改 title,本笔补齐)。
- dispatch.ts 分工缺省角色「执行」→「执行者」。
- cli.ts dispatch/report --help 说明段产品词化(命令名/参数名不动)。

tsc/build/相关测试(50+182)/diff --check 全绿。
narration.ts+goal-watchdog.ts 的 mode:'自动对账' 类型字面量涉及已入库数据兼容,留给 codex 判断。
# Conflicts:
#	src/core/dashboard-ipc-server.ts
#	src/daemon.ts
P1 第 3 件(契约与 codex 已钉):systemRisk 行给「重派/升级给人」、blocked 行给
「处理求助/升级给人」按钮,与既有轻量唤醒按钮并列(.attn-action 同款式,零新 CSS)。

点击构造 [panel-action v1] 文本块(action/taskId/reason/instruction,reason 预填
disposition.reason 稳定码)POST 到既有 /api/goals/:goalChatId/decision——dashboard
不直接写账不直接派活,由监管者核对交付记录后执行真命令并留痕,归因干净。
按钮态:下发中…→已下发 ✓/失败(title=错误);no_supervisor 给人话报错。

tsc/build 绿;产物落 chunks/goals-*.js(React 迁移后 code-splitting)。
L2 侧 [panel-action v1] 识别指引由 codex 补。
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