diff --git a/docs/README.md b/docs/README.md index 5b744eb..2d73928 100644 --- a/docs/README.md +++ b/docs/README.md @@ -64,7 +64,7 @@ Graph Agent 架构重构(27 系列)。**已完成的阶段文档已移入 `d | [45-graph-run-node-liveness-recovery.md](./todo/system-design/45-graph-run-node-liveness-recovery.md) | ✅ **Part A/B/C 已实现**(2026-08-31),**Part D 有意延后**。真实事故:h3video 公司一个 `agent_call` 节点卡在 `status=running`、背后 CLI 进程已死,服务器主进程没重启——心跳(纯 LLM 驱动)、开机一次性孤儿扫描、goal/task 巡检(判据是 `blocked` 不是 `running`)、wallclock 看门狗(按预算不按存活)四层机制全盖不到;`ceo` 被转发问题后自己绕过引擎拉会话,陷入死循环。**Part A**:调查发现 `SessionPool` 是每次调用现造即回收的一次性对象,没有可查询的存活注册表,原设计不可行,改用「运行时长超阈值(默认 25 分钟)+ 只扫 `agent_call` 节点」,新函数 `sweepStaleRunningAgentCalls`/`startStaleAgentCallWatchdog`(`graph-engine.js`),5 条新单测。**Part B**:`force-fail` 端点(42 号文档已实现)本身就够用,零新后端代码,只在 `ceo/tools.md` 补了使用文档 + 明确禁止绕过引擎自拉会话。**Part C**:`ceo/heartbeat.md` 同步更新。**Part D**(节点补显式心跳字段,消除 Part A 阈值判据的误伤局限)未做,见文内「实现记录」的理由 | | [47-itemized-subtask-materialization-goal-benchmark-fallback.md](./todo/system-design/47-itemized-subtask-materialization-goal-benchmark-fallback.md) | ✅ **已修复**(2026-09-02)。46 号文档 Phase 0 真实回归坐实:`acceptance.md` 在当前 `workgraph-planning` 常规划出的 org-planner 子图形状(prd/sdd/goal-benchmark/planner-validator)下结构性产不出来,`itemized` 子任务物化 0 命中。修法:物化逻辑新增兜底,`acceptance.md` 缺失时改读已被 44 号文档证实稳定产出的 `goal-benchmark.json` 的 `dimensions[]`。`task-decomposition-materialize.test.js` 5 条全过(2 条新增,用真实回归捕获的产物做测试数据) | | [46-mandatory-planning-and-task-decomposition.md](./todo/system-design/46-mandatory-planning-and-task-decomposition.md) | 📝 **Phase 0-3 v2 均已实现且做过真实回归**(2026-09-02)。承接 43 号文档的追问:任务规划应是每个任务默认前置步骤,来源覆盖模型自身能力/知识库经验/线上 deep research。**Phase 1**:新 agent `subtask-planning`,钩在 `enqueueTaskRun`(显式 opt-in),产物写共享 `artifacts/subtask-plan.json`。**Phase 2**:经验检索(扫已雇 agent 的 dream 沉淀)+ 按需 deep research(图外调用 org-hunt,默认关闭);真实回归揪出一个关键词打分噪音压过信号的真缺陷并修复。**Phase 3 走过一轮推翻重来**:v1("认领"pull 语义 + CAS)做完真实回归才发现"认领后没有触发源",讨论后确认"多方竞争"场景根本不存在(只有 CEO 一人拍板)——**v2 改成 CEO 心跳直接发现+派发(push)**,子任务走一张现造的轻量 work graph(`org-coder`+`org-validator` 两节点,跳过公司完整 org graph 的规划仪式)。v2 真实回归:CEO 心跳真的发现并派发了子任务,`org-coder` 产出质量达标,但 `org-validator` 卡在硬依赖的 `acceptance.json`(只有 `org-planner` 会产出,轻量图没有这个节点)——**验证这一半有真实的结构性缺口,后续方向见 [48 号文档](./todo/system-design/48-task-acceptance-contract-binding.md)**。验证过程顺手修了一个更大范围的真实 bug:两套心跳系统的自环调用一直缺内部认证令牌、暗中 401。Phase 4(独立开发验证循环)、Phase 5(看板 UI)未开工 | -| [48-task-acceptance-contract-binding.md](./todo/system-design/48-task-acceptance-contract-binding.md) | ✅ **主力机制已实现**(decomposed_work 链路,2026-09-02)。承接 46 号文档 Phase 3 v2 真实回归发现的 `org-validator` 死锁:验收契约的产出被硬绑在"公司完整 org graph 一定会跑到 org-planner"这个假设上,不满足这个假设的执行路径(如轻量子任务图)无契约可读、退无可退。方案:消费端(validator 只认结构化契约、不自由判断)保持硬约束不变,产出端改成"每个 decomposed_work 子任务创建时必须 1:1 带一份验收契约"(`tasks.acceptance_contract` + `createTask` 统一校验 + `enqueueTaskRun` 落盘)。真实起 `subtask-planning` agent 验证时推翻了设计稿里的字段形状假设(真实产出是 benchmark 段本身,不是三段式 wrapper)并已修正——设计文档留痕、代码按修正后的形状实现,见文档「实现记录」。`task-planning`/`org-planner`/人类快速派单三条创建路径本轮不动;安全网(发现时就地补契约)仍只是原则 | +| [48-task-acceptance-contract-binding.md](./todo/system-design/48-task-acceptance-contract-binding.md) | ✅ **已实现并完成真实端到端回归**(decomposed_work 链路,2026-09-02,回归补于 2026-09-03)。承接 46 号文档 Phase 3 v2 真实回归发现的 `org-validator` 死锁:验收契约的产出被硬绑在"公司完整 org graph 一定会跑到 org-planner"这个假设上,不满足这个假设的执行路径(如轻量子任务图)无契约可读、退无可退。方案:消费端(validator 只认结构化契约、不自由判断)保持硬约束不变,产出端改成"每个 decomposed_work 子任务创建时必须 1:1 带一份验收契约"(`tasks.acceptance_contract` + `createTask` 统一校验 + `enqueueTaskRun` 落盘)。真实起 `subtask-planning` agent 验证时推翻了设计稿里的字段形状假设(真实产出是 benchmark 段本身,不是三段式 wrapper)并已修正。**补跑的真实端到端回归**:真实 `org-coder` → `org-validator` 全程跑通,`org-validator` 判词 `fail_reason=null`,不再 `input_underspecified`——过程中撞见两个纯属验证方法论本身(非代码)的坑(没起真实 API 服务、agent 子进程与服务进程不同源导致内部令牌对不上),修法与教训见文档「真实端到端回归补跑」。`task-planning`/`org-planner`/人类快速派单三条创建路径本轮不动;安全网(发现时就地补契约)仍只是原则 | | [43-task-decomposition-granularity.md](./todo/system-design/43-task-decomposition-granularity.md) | ✅ **Phase 0-4 全部已实现**(2026-08-31)。回应"org-planner 出 brd/prd/sdd 后,任务看板能不能展示成 task/sub-task 树,coder-validator 按清单逐条开发验收"。`tasks` 加 `acceptance_mode`/`parent_task_id`(后者复用既有列);`task-planning` 判断要不要拆(硬规则 5 开例外让 LLM 写这一个字段);org-planner 收尾把 `acceptance.md` 物化成子任务;`coder-validator` 新增 `task subtask-result` 工具逐条上报,`settleNode()` 新增闸门强制核对子任务全 `completed` 才认 success;看板加子任务展示。**落地时订正了一处原计划想岔的架构假设**:org-planner 在 org 层永远是 `subgraph` 节点(不是 agent_call),真跑数据显示它当前展开成的 `planner.json` 5 节点子图里,`goal-benchmark`/`planner-validator` 两节点常被 skip,`acceptance.md` 未必真的产出——钩子已改成扫子 run 全节点、找不到就优雅降级为 holistic,不阻塞主产出,但"子任务一定会长出来"这件事目前是尽力而为不是保证,见文档"实现记录"一节的遗留风险说明。全量单测 1120/1123 通过(3 个失败与本次改动无关,本次改动前即存在) | ## todo/future/(暂缓,非当前重心) diff --git a/docs/todo/system-design/48-task-acceptance-contract-binding.md b/docs/todo/system-design/48-task-acceptance-contract-binding.md index c166ef3..057006f 100644 --- a/docs/todo/system-design/48-task-acceptance-contract-binding.md +++ b/docs/todo/system-design/48-task-acceptance-contract-binding.md @@ -1,7 +1,7 @@ # 48 · Task/Subtask 验收契约 1:1 强绑定 -> **状态**: ✅ 已实现(主力机制,decomposed_work 链路)。安全网仍只是原则,未写代码。 -> **日期**: 2026-09-02 +> **状态**: ✅ 已实现并完成真实端到端回归(decomposed_work 链路,含真实 `org-coder`/`org-validator` CLI 全程跑通,`org-validator` 判词 `fail_reason=null`,不再 `input_underspecified`)。安全网仍只是原则,未写代码。 +> **日期**: 2026-09-02(补充真实回归:2026-09-03) > **触发场景**: 46 号文档 Phase 3 v2 真实回归时,`org-validator` 在轻量子任务图(`org-coder` 实现 → `org-validator` 验证,跳过公司完整 org graph 的 `org-planner` 规划仪式)里卡死——`goal-contract.js validate --run-dir` 报 `input_underspecified`(没有 `acceptance.json`),`org-validator` 按它自己的硬指令"退 `org-planner` 重跑",但这张轻量图里**压根没有 `org-planner` 节点**,退无可退。追问下去牵出一个更根本的问题:"验收契约由谁产出"这件事目前完全绑死在"公司完整 org graph 一定会跑到 `org-planner`"这个假设上,任何不满足这个假设的执行路径都会撞上同一类死锁。 --- @@ -101,9 +101,9 @@ ## 验收标准(后续实现完要检查的) -1. `createTask` 不给 `acceptance_contract` 直接创建 `decomposed_work` 子任务——应该被拒绝(如果按主力机制彻底做,`decomposed_work` 这个类型应该强制要求这个字段非空)。 -2. 真实回归:重复 46 号文档 Phase 3 v2 那次卡死的场景(单位换算任务的轻量图),这次 `org-validator` 应该能读到 `artifacts/acceptance.json`、真正跑一次 `goal-contract.js validate` + `run`,不再是 `input_underspecified`。 -3. 确认 `org-coder`/`org-validator` 全程不需要感知这份契约是"这次由 subtask-planning 供的"还是"平时 org-planner 供的"——两条路径对它们完全透明。 +1. ✅ `createTask` 不给 `acceptance_contract` 直接创建 `decomposed_work` 子任务——应该被拒绝(如果按主力机制彻底做,`decomposed_work` 这个类型应该强制要求这个字段非空)。单测覆盖,见「实现记录」。 +2. ✅ 真实回归:重复 46 号文档 Phase 3 v2 那次卡死的场景,这次 `org-validator` 真的读到了 `artifacts/acceptance.json`、真正执行了判分逻辑,`fail_reason=null`,不再是 `input_underspecified`。见「真实端到端回归补跑(2026-09-03)」。 +3. ✅ 确认 `org-coder`/`org-validator` 全程不需要感知这份契约是"这次由 subtask-planning 供的"还是"平时 org-planner 供的"——两条路径对它们完全透明;`org-validator` 判词自己也确认了这一点("非本节点自行发明标准——维度/权重/及格线均来自契约原文")。 --- @@ -132,8 +132,19 @@ 2. 真实起 `p2verify0902` 公司里已雇佣的 `subtask-planning` agent(真实 Claude CLI 调用,不是注入桩),给一条真实 task("实现摄氏度转华氏度的转换函数"),验证 agent 真的会按新 `agent.md` 交出 `acceptance` 字段——交了,且格式如上一节所述(暴露了设计文档与真实产出的形状分歧,已修正)。 3. 用真实 LLM 那次调用的原始产出(未修改一个字)跑一遍完整链路:`materializeDecomposedSubtasks` → `createTask`(校验+落库)→ 模拟 `enqueueTaskRun` 的写入段 → `goal-contract.readContract` + `validateContract`,结果 `{"ok":true,"errors":[]}`——这就是"`org-validator` 不会再撞 `input_underspecified`"这件事的直接证据(验收标准 2 的核心机制)。 +### 真实端到端回归补跑(2026-09-03):`org-validator` 真的判过了 + +用户明确要求补一次更干净的真实回归,跑通了完整链路,验收标准第 2 条现在**已兑现**: + +1. 真实建一条全新任务("实现字符串反转函数")、真实起 `subtask-planning` agent(真实 CLI,不是注入桩)分解出 1 条 `decomposed_work` 子任务,`acceptance` 真实产出为 `{"dimensions":[{"name":"普通字符串反转正确","weight":0.6,...},{"name":"空字符串正确处理(返回空字符串,不抛异常)","weight":0.4,...}],"overall_pass_threshold":80}`。 +2. 真实调用 `POST /tasks/:taskId/enqueue`(走 CEO 真实用的 `x-internal-token` 认证路径),起了真实的轻量图(`org-coder` → `org-validator`,两个真实 `agent_call` 节点)。 +3. **过程中撞见两个纯属本次验证方法论本身的坑,均与 48 号文档代码无关**:① 第一次没起真正的 API 服务(只在脚本进程里跑图引擎),`org-coder` 完工后调 `bin/lemon.js task transition`/`report_task` 够不着 `localhost:3010`,连续 4 次 `infra_flake` 撞熔断硬顶、卡在 `blocked`;② 补起服务后单独重试,`org-coder` 子进程作为**另一个独立进程**(我的重试脚本)的子进程,没能继承真实服务进程自己生成的 `LEMON_INTERNAL_TOKEN`(该 token 是每进程随机生成、只有其自身子进程才认得),打 API 仍是 401。两次都被真实 agent 自己的诊断日志准确点破根因(org-validator 后来的判词原话也复述了这段"记账问题",并明确判定"不在本节点验收范围内")。最终解法:让驱动脚本 `require('server/index.js')` 在**同一个进程**里把真实服务和图引擎重试跑在一起,`org-coder` 子进程这才拿到匹配的 token。 +4. 修好拓扑问题后重跑,`org-coder` 真实实现完成,`org-validator` 真实执行——**读到了 `acceptance.json`、真的走了 `goal-contract` 的判分逻辑,两条维度都打了分(100/100),`fail_reason` 为空,不是 `input_underspecified`**。`org-validator` 的判词原文还独立追加了 7 组自己写的测试用例复核交付物,附带一句准确的旁注:"本次 acceptance.json 仅含 benchmark 段(created_by=subtask-planning,48 号文档轻量子任务图路径:org-coder 不产出自报分,org-validator 自行取证判分,非本节点自行发明标准——维度/权重/及格线均来自契约原文,未改动)"。 +5. 最终态:`graph_run_nodes` 两个节点均 `success`,`graph_runs.run_status = completed`,task `status = completed`。 + +**验证方法论本身留下的经验**(写下来免得下次重犯):真实驱动图执行必须让 agent 子进程与提供 API 的服务进程同源(同一个 Node 进程内 `require('server/index.js')`),否则 `LEMON_INTERNAL_TOKEN` 天然对不上、agent 侧的 CLI 工具调用会全部 401——这不是本次代码的缺陷,是"拿脚本模拟真实拓扑"这件事本身的一个通用坑,下次做类似真实回归可以直接照这个模式起。 + ### 仍未做的(遗留,按「明确不做」的既有边界) -- **没有重跑一次完整的真实 org-coder → org-validator 轻量图**(46 号文档 Phase 3 v2 撞见死锁的那条完整链路)。已验证的是"契约会被正确产出、正确落盘、`goal-contract.validateContract` 判为合法"——这是 `org-validator` 卡死的根因链条上唯一改动的环节,其余环节(`org-coder`/`org-validator` 怎么读契约、怎么判卷)本次未改一行,46 号文档 Phase 3 v2 的真实回归已经验证过 `org-coder` 这一段能正常工作。如果想要不留死角的确认,需要用户手动挑一个任务,让 CEO 心跳把新物化的 `decomposed_work` 子任务真派发一遍,全程跑完(含真实 `org-coder`/`org-validator` CLI 调用,成本较高,未在本轮自动跑)。 - 安全网("发现缺失时就地补一份")仍然只是原则,未写代码——`decomposed_work` 链路走主力机制已完全覆盖,原设计判断不变。 - `task-planning`/`org-planner` 的 `acceptance_item` 物化/人类快速派单三条创建路径的契约强制——本轮范围外,未动。