# 人工介入中心方案 ## 目标 将 Task 阻塞后的人工介入提升为 Runtime 的统一领域事实。叶子只提交阻塞事项;Web 看板与钉钉本人私聊是两个平级处理入口;钉钉桥接是上层通知适配器,不拥有处理状态。内部持久化字段和 HTTP 路径继续沿用 `authorization` 命名,以兼容历史数据和既有客户端。 ## 状态模型 - 内部处理状态:`pending -> approved | rejected`,分别表示继续 Task 或不执行请求动作;终态不可被第二个通道覆盖。 - 钉钉投递状态:`pending-send -> waiting-reply -> recalled`,仅描述通知载体,不决定人工处理结果。 - `blockerCategory + 规范化 requestedAction` 形成范围指纹;同一 Task、同一范围只存在一个有效人工介入事项。 - 处理记录保存来源(`web` 或 `dingtalk`)、时间、意见和钉钉引用消息标识;Task、Goal 和人工介入事项在同一 Runtime 串行事务中推进。 ## 两条闭环 1. 叶子提交阻塞事项,Runtime 原子创建人工介入记录并展示到“人工介入”。钉钉桥接发现待发送记录后,发送本人私聊并等待引用回复;独立的个人 IM 实时订阅按被引用消息的 `messageId` 精确关联回复,通过统一处理命令落盘并恢复原 Task。历史查询只负责进程离线窗口恢复,不能作为实时回复唯一入口。 2. 真人在 Web 选择继续任务或不执行,也可填写处理意见。Runtime 先写入终态,将选择和完整原文送入叶子会话,再恢复原 Task 的 Goal;叶子必须结合人工意见核验当前事实后决定下一步。钉钉桥接随后停止轮询。若私聊消息已经发送,则按既有竞态规则撤回并记录结果。 ## 竞态与幂等 - 每次 DWS 搜索、发送、记录投递、读取回复前后都回读申请当前状态。 - Web 与钉钉同时处理时,首个终态获胜;相同决定重复提交返回原记录,不同决定返回冲突。 - Web 在 DWS 发送过程中处理时,发送可能无法取消;发送完成后立即回读状态并撤回真实消息。 - Goal 自动续跑与真人处理并发时,处理原文必须先进入叶子会话队列,之后才能恢复 Goal,避免续跑轮次基于旧现场再次提交阻塞。 - Runtime 重启后从持久 Task 人工介入历史恢复事项;不会依赖内存等待进程。 - 群消息 listener 与个人人工介入回复 listener 独立运行;任一已配置实时入口未 `ready` 时,DWS bridge 健康状态不得报告正常。 ## HTTP 与页面 - `GET /state/authorizations`:读取全部人工介入事项及关联 Task/群信息;路径保留历史技术命名。 - `POST /authorizations/:requestId/decision`:提交 `{ decision, comment }`,来源固定为 Web。 - Header 在“任务看板”右侧增加“人工介入”。页面按等待处理、已继续、不执行展示列表;等待项提供继续任务、不执行和处理意见输入。 ## 验收 - 新事项立即出现在 Web;Web 处理后恢复同一 Task。 - 钉钉引用处理与 Web 使用同一状态机。 - Web 先处理时不再发送;发送竞态发生时撤回已发消息并停止轮询。 - 重复提交、重复处理、双通道竞态均不产生第二个人工介入事项或覆盖首个终态。