Commit f0468e9b41e967cf63f876bbf771c67b1871947e
1 parent
f578c0e4
docs(task-decomposition): 46号文档 Phase 3 v2 设计+实现记录
标记 v1 详细设计/实现记录为废弃(原样保留,不删——是决策过程的第一手 记录:真实回归暴露认领没有触发源的真问题,才推翻重来)。补 Phase 3 v2 章节:CEO push 派发的讨论过程、落地清单、一条没有真实验证过的假设 (org-coder/org-validator 在轻量图里没有 brd/prd/sdd 上游产物时表现 如何)、还没做真实端到端回归的说明。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H85SS5hVC4u1PbVhjmrhH8
Showing
1 changed file
with
42 additions
and
3 deletions
docs/todo/system-design/46-mandatory-planning-and-task-decomposition.md
| 1 | 1 | # 46 · Planning 应作为每个任务的默认前置步骤,覆盖三类信息来源 |
| 2 | 2 | |
| 3 | -> **状态**: 📝 设计中——四个关键决策已拍板。**Phase 0 已完成**(2026-09-01 真实回归验证):链路能收敛、44 号文档修复确认生效,发现的独立缺陷已拆到 [47 号文档](./47-itemized-subtask-materialization-goal-benchmark-fallback.md) 并修复。**Phase 1 代码已实现**(2026-09-02,见「Phase 1 实现记录」):新 agent `subtask-planning` + `enqueueTaskRun` 挂钩(收窄为显式 opt-in)+ 复用 R-9.3 任务级共享索引对接下游,单测覆盖,**但还没做真实端到端回归**(且过程中出过一次真实 CLI 子进程泄漏事故,已修复并加了测试环境防护,见该节)。**Phase 2 已实现且已做真实端到端回归(收窄版,2026-09-02,见「Phase 2 实现记录」)**:经验检索(新查询能力,扫已雇 agent 的 dream 沉淀)+ 按需 deep research(图外调用 org-hunt,默认关闭)。真实回归第一次就跑出一个真缺陷(关键词打分噪音压过信号,导致"经验库有答案"分支误触发)并已修复验证;两条真实任务的分解内容质量都好,但 deep research 分支目前更接近"多问一个角色的第二意见"而非真的做了外部检索(org-hunt 那次调用没有工具调用痕迹),如实记在实现记录里。"历史同类 goal 检索"这条来源本轮明确收窄未做。**Phase 3 代码已实现(2026-09-02,见「Phase 3 实现记录」)**:`tasks` 表加 `subtask_kind`/`claimed_at`,认领 CAS(`claimSubtask`/`unclaimSubtask`,全仓库第一个"认领"语义写入)+ 物化(`materializeClaimableSubtasks`)+ 两个新路由 + `settleNode` 闸门收窄(必须同步改,已用回归测试固定)。认领后复用现成的 `executor='agent'` 派单闭环,**没有碰 `graph-engine.js` 的调度逻辑**。单测覆盖(23+ 条新增),**但还没做真实端到端回归**——尤其"认领后 org-coder 自己怎么干"这一段完全没有验证质量的机制,设计阶段就写明"跑得糙"。Phase 4(独立开发验证循环)、Phase 5(看板 UI)仍未设计,留给后续 | |
| 3 | +> **状态**: 📝 设计中——四个关键决策已拍板。**Phase 0 已完成**(2026-09-01 真实回归验证):链路能收敛、44 号文档修复确认生效,发现的独立缺陷已拆到 [47 号文档](./47-itemized-subtask-materialization-goal-benchmark-fallback.md) 并修复。**Phase 1 代码已实现**(2026-09-02,见「Phase 1 实现记录」):新 agent `subtask-planning` + `enqueueTaskRun` 挂钩(收窄为显式 opt-in)+ 复用 R-9.3 任务级共享索引对接下游,单测覆盖,**但还没做真实端到端回归**(且过程中出过一次真实 CLI 子进程泄漏事故,已修复并加了测试环境防护,见该节)。**Phase 2 已实现且已做真实端到端回归(收窄版,2026-09-02,见「Phase 2 实现记录」)**:经验检索(新查询能力,扫已雇 agent 的 dream 沉淀)+ 按需 deep research(图外调用 org-hunt,默认关闭)。真实回归第一次就跑出一个真缺陷(关键词打分噪音压过信号,导致"经验库有答案"分支误触发)并已修复验证;两条真实任务的分解内容质量都好,但 deep research 分支目前更接近"多问一个角色的第二意见"而非真的做了外部检索(org-hunt 那次调用没有工具调用痕迹),如实记在实现记录里。"历史同类 goal 检索"这条来源本轮明确收窄未做。**Phase 3 走过一轮推翻重来(2026-09-02)**:v1("认领"pull 语义 + CAS)已实现并做过真实回归,但回归本身就暴露出"认领后没有触发源"的真问题,讨论后发现"认领"要解决的"多方竞争"场景根本不存在(只有 CEO 一人拍板要不要派)——**v2 改成 CEO 心跳直接发现+派发(push),删掉了 v1 的整套认领机制**,改为 CEO 判断节奏后调现成的 `/enqueue`,子任务默认走一张现造的轻量 work graph(`org-coder` 实现 + `org-validator` 验证两个节点,跳过公司完整 org graph 的规划仪式),效果不理想再升级成完整 org graph(见「Phase 3 v2」)。v1 设计和实现记录原样保留、标了废弃(留作决策过程的第一手记录,不是白做)。v2 单测覆盖,**但还没做真实端到端回归**——尤其"org-coder/org-validator 在轻量图里没有 brd/prd/sdd 上游产物时表现如何"是一条明确写清楚、没验证过的假设。Phase 4(独立开发验证循环)、Phase 5(看板 UI)仍未设计,留给后续 | |
| 4 | 4 | > **日期**: 2026-09-01 |
| 5 | 5 | > **触发场景**: 用户复盘 [43 号文档](./43-task-decomposition-granularity.md)(goal 分解粒度)时,追问"为什么本地和线上所有公司的任务看板都没有子任务分解"。核实后发现:不是巧合或配置遗漏,`acceptance_mode='itemized'` 在当前系统里**从未被真正触发并落库过一次**——追问引出了比 43 号文档更大的架构缺口:43 号文档只解决"验收要不要拆细",而用户认为**规划(把一件事拆成第一步/第二步/第三步)本身应该是每个任务默认必经的前置步骤,不是一个可选的、可以被判"不需要"的例外**。 |
| 6 | 6 | > **格式对齐** `27-requirement.md`/`43-task-decomposition-granularity.md`:原话照搬 + 结构化复述,现状调研另起一节。 |
| ... | ... | @@ -318,7 +318,9 @@ goal 创建 |
| 318 | 318 | |
| 319 | 319 | --- |
| 320 | 320 | |
| 321 | -## Phase 3 详细设计(2026-09-02,设计已细化,未实现) | |
| 321 | +## Phase 3 详细设计(v1,2026-09-02,⚠️ 已废弃——见下方「Phase 3 v2」) | |
| 322 | + | |
| 323 | +> **这一版设计和它对应的实现记录整段废弃,但原样保留在下面**(不删):真实回归发现"认领后没有触发源"这个真问题之后,跟用户讨论出的根本原因是——"认领"(pull,CAS 抢占)解决的是"多方竞争同一条任务",但实际场景里从头到尾只有 CEO 一个人拍板要不要派、派给谁,压根不存在竞争,CAS 是在给不存在的问题找解。v2(本文档更后面)改成 CEO 直接派发(push),砍掉了 `claim`/`unclaim` 两个端点和 `claimable`/`claimed` 两个状态。留着 v1 不删是因为这段"先做认领、跑真实回归、发现问题、推翻重来"的过程本身有价值——**它是这次为什么改主意的第一手证据**,删掉只留结论会让人以为 v2 是一次性设计对的。 | |
| 322 | 324 | |
| 323 | 325 | ### 前置决策(跟用户对齐) |
| 324 | 326 | |
| ... | ... | @@ -399,7 +401,9 @@ WHERE id=? AND subtask_kind='claimable_work' AND status='claimable' |
| 399 | 401 | 4. 认领成功后,这条行是否真的能走完 `executor='agent'` 的既有生命周期(`org-coder` 自己完成、`/task/report` 上报、状态正确落到终态)? |
| 400 | 402 | 5. 非白名单角色尝试认领,是否被正确拒绝? |
| 401 | 403 | |
| 402 | -## Phase 3 实现记录(2026-09-02,代码已实现,未做真实端到端回归) | |
| 404 | +## Phase 3 实现记录(v1,2026-09-02,⚠️ 代码已被 v2 替换/删除——见「Phase 3 v2」) | |
| 405 | + | |
| 406 | +> 本节记录的代码(`claimSubtask`/`unclaimSubtask`/`/claim`/`/unclaim`/`claimable`/`claimed`)**已经不在仓库里**。保留这份记录是因为下面的"真实端到端回归"小节记录了促成 v2 拍板的两个关键发现(认领没有触发源、`/task/report` 耦合了非 graph 回调),这两个发现本身仍然成立、仍然是 v2 存在的理由,不能删。 | |
| 403 | 407 | |
| 404 | 408 | ### 落地清单 |
| 405 | 409 | |
| ... | ... | @@ -432,3 +436,38 @@ WHERE id=? AND subtask_kind='claimable_work' AND status='claimable' |
| 432 | 436 | |
| 433 | 437 | - 现在"认领后怎么干"完全靠 `org-coder` 自主决定,质量没有保障——要不要给 `claimed` 状态补一段正式的"开发 + 验证"生命周期?如果要,是新起一次简化版 work-graph run,还是在现有 `executor='agent'` 闭环里加一道可选的验证钩子(成本更低,但仍然不是"独立 run")?两个方向哪个都不是这次 Phase 3 该决定的事,需要单独评估。 |
| 434 | 438 | - 子任务级预算:`executor='agent'` 现有闭环目前完全没有预算概念(`budget_json` 只在 `executor='graph'` 的 run 上有意义)——如果 Phase 4 决定要给认领的子任务设预算,需要先补这个空白。 |
| 439 | + | |
| 440 | +--- | |
| 441 | + | |
| 442 | +## Phase 3 v2:CEO 直接派发(2026-09-02,代码已实现,未做真实端到端回归) | |
| 443 | + | |
| 444 | +### 为什么推翻 v1(讨论过程,不是事后诸葛亮) | |
| 445 | + | |
| 446 | +v1 真实回归跑完,用户追问"认领后没有触发源,谁来触发"——讨论过程: | |
| 447 | + | |
| 448 | +1. 用户最初设想:每个候选角色(`org-coder`)自己开心跳,扫描"有没有可认领的、匹配我角色的子任务",够条件自己 `/claim`。 | |
| 449 | +2. 反对意见(用户自己提出):心跳生命周期管理的开销是真实问题——几百个 agent 都开心跳,谁来决定什么时候启、什么时候停?如果还是需要 CEO 来管这些心跳的启停,那跟 CEO 直接扫一遍指派没有区别,等于多绕了一层。 | |
| 450 | +3. 拍板:**CEO 心跳统一发现 + 直接指派(push),不需要认领机制**。CEO 本来就有心跳、本来就在盯 task 板,成本最低;CEO 还能顺带控制派发节奏(先放哪批、等哪批)。 | |
| 451 | +4. 顺着这条拍板往下查:现有 `enqueueTaskRun`(起 org/work graph run 的唯一入口)默认假设自己服务顶层 task(硬要求 `status==='draft'`,起 run 时把 status 强改成 `'queued'`)——这个状态转移**跟 v1 的 `claimable`/`claimed` 状态机直接冲突**(CAS 会静默失败)。查证过程中发现:v1 设计"认领"要解决的"多方竞争"场景,在"CEO 一人拍板"的模型下根本不存在——CAS 是在给不存在的问题找解,不是保留价值、只是不再需要的复杂度。**拍板删除 `claim`/`unclaim` 和相关状态**,改成"物化即普通 draft task,CEO 判断节奏后调现成的 `/enqueue`"。 | |
| 452 | +5. 顺带定了执行载体:"先跑一个轻量的子任务图看效果,遇到质量问题再升级成完整 org graph"——不是先验地假设哪个对,是留一个可以真实观察、按需升级的路径。 | |
| 453 | + | |
| 454 | +### 落地清单 | |
| 455 | + | |
| 456 | +| 内容 | 位置 | | |
| 457 | +|---|---| | |
| 458 | +| 删除的代码 | `claimSubtask`/`unclaimSubtask`(`work-graph-db.js`)、`POST /claim`/`POST /unclaim`(`company.js`)、`CLAIMABLE_WORK_ROLES` 白名单、`claimable`/`claimed` 两个 status 值、`claimed_at` 列(连同批 6 迁移里加它的那部分) | | |
| 459 | +| 改名 | `subtask_kind` 的枚举值 `'claimable_work'` → `'decomposed_work'`(连同 `materializeClaimableSubtasks` → `materializeDecomposedSubtasks`)——"认领"这个词不再准确,趁还没发布改掉比以后改省事 | | |
| 460 | +| 物化行为简化 | `materializeDecomposedSubtasks` 物化出的行现在就是普通 `status='draft'` task(不再有专属初始状态),`executionMode='auto'`(CEO 一旦 enqueue 就该一路跑完,不停在入口等人工 `/next`) | | |
| 461 | +| **新增:递归防护** | `triggerSubtaskPlanning`(`subtask-planning.js`)新增 `if (task.parent_task_id) return`——这是设计 v2 时才发现的真实漏洞:CEO 对 `decomposed_work` 子任务调 `/enqueue` 时,该端点默认 `withSubtaskPlanning:true`,不挡的话会对一条已经是"第几步怎么做"粒度的子任务再拆一层,浪费调用还可能无限递归。已用测试验证挡住 | | |
| 462 | +| **新增:轻量子任务图** | `createDecomposedWorkGraph`(`work-graph-db.js`)——现造一张两节点 work graph(`impl`=`org-coder`、`validate`=`org-validator`,都是 `agent_call` 不是 `subgraph`),跳过公司完整 org graph 的 ceo→cto→org-planner→org-coder→org-validator 规划仪式。理由:`subtask-planning` 已经把"怎么做"拆到这一步的粒度了,再走一遍 org-planner 的 brd/prd/sdd 纯属重复劳动 | | |
| 463 | +| `enqueueTaskRun` 图选择 | `company.js`:`taskRow.subtask_kind === 'decomposed_work'` 时用 `graphKind:'work'` + 现造的图;否则行为完全不变(`graphKind:'org'` + `companyId`),回归测试覆盖 | | |
| 464 | +| CEO 心跳/工具 | `system/agents/ceo/heartbeat.md`、`tools.md` 补了"查 `?subtaskKind=decomposed_work&status=draft`、按节奏 `/enqueue`"的指令——`enqueue` 是 CEO 已经被授权自主调用的现成端点,子任务用同一条权限,不用新开一条 | | |
| 465 | +| 测试 | 删除 `task-pool-claim.test.js`(v1 的认领测试,随代码一起废弃),新增 `task-decomposition-dispatch.test.js`(12 条:DB 层 `createTask` 校验/`materializeDecomposedSubtasks`/`createDecomposedWorkGraph`,路由层 `enqueueTaskRun` 图选择回归 + decomposed_work 分支),更新 `task-decomposition-validator-gate.test.js`/`task-decomposition-materialize.test.js` 的措辞。全量单测 1191 条,1189 passed(唯二失败与本次改动无关、改动前即存在),零残留 CLI 子进程 | | |
| 466 | + | |
| 467 | +### 一处没有真实验证过的假设,如实写在这里 | |
| 468 | + | |
| 469 | +`org-coder`/`org-validator` 平时**只在"公司完整 org graph 的 subgraph 节点"这个上下文里被调用过**——它们的 `agent.md`/`tools.md` 有没有隐含"前面一定有 brd/prd/sdd 产物"这种假设,直接当轻量图里的裸 `agent_call` 节点调用,会不会读不到预期上下文、表现异常——**没有实测过**。图的机制本身(建图、起 run、CAS 判据不冲突)单测覆盖了,但"跑起来输出好不好"是内容问题,机制通不通答不了这个问题。这正是用户拍板"先跑轻量版看效果"这句话里"看效果"的意思——下一步真实回归要验的就是这个。 | |
| 470 | + | |
| 471 | +### 还没做真实端到端回归 | |
| 472 | + | |
| 473 | +机制层面(物化→ CEO 心跳发现→ `/enqueue` →轻量图跑起来→状态收敛)已经用单测验证,但**没有一次真实的 CEO 心跳发现并派发过一条真实的 `decomposed_work` 子任务**。真实回归要检查:① CEO 读了 `heartbeat.md` 新加的这段指令后,是否真的会去查、去判断节奏、去调 `/enqueue`(提示词有没有写清楚,是纯 prompt 工程问题,不是这次代码改动能保证的);② 轻量图跑完,`org-coder`/`org-validator` 在没有 brd/prd/sdd 上游产物的情况下产出质量如何(就是上面那条没验证过的假设);③ 一条 task 拆出多条子任务、CEO 一次性全部 `enqueue` 后,`task-scheduler` 的依赖阻塞和并发槽位是否真的像讨论时判断的那样"自动处理好节奏",不需要 CEO 操心。这次不建议顺手跑(吸取 Phase 1/2 的教训),留给用户后续决定何时验证。 | ... | ... |