Commit f7103fd723bee7a0795e63c6f5832bfdc08ade08
1 parent
41a0492e
Org Graph unification Phase 2c §7.1: add work-graph-node-review skill
New skills/work-graph-node-review/SKILL.md documents the methodology for judging a self-planned Work Graph node's output (real structured content vs. narrative prose standing in for it) -- distilled from the real narrative-output incident (docs/todo/validator-test-script-narrative- output-issue.md §6) and the conditional-not-assertive phrasing lesson already applied in _NARRATION_RETRY_HINT. Scoped explicitly against planner-output-review (Planner's fixed doc chain, task-level gate), qa-task-validation (overall task acceptance), and skill-defect-remediation (rewriting a Skill file itself) -- this one judges a single self-planned node's output, nothing broader. Wired into roles/planner_reviewer, roles/coder, roles/validator role.json skills lists. .claude/agents/*.md left untouched -- that's a deploy-time artifact compiled from role.json by agent_profile.deploy_agent_definitions(), not hand-synced (confirmed stale already: coder.md is missing an even earlier Phase 2a skill addition). This completes Phase 2c (§2.1/§2.2/§6/§7.1 all implemented; §7.2's analysis was completed earlier without needing a code change). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Showing
5 changed files
with
88 additions
and
4 deletions
docs/todo/org-graph-unification-phase2-universal-self-planning-design.md
| ... | ... | @@ -5,7 +5,11 @@ |
| 5 | 5 | **依赖**: 硬依赖 Phase 1(`docs/done/org-graph-unification-phase1-review-gate-design.md`)——已实现(commit `ea19c7d`,2026-08-08)。§9 接口核对清单已于 2026-08-12 逐条核对完成:三项全部吻合(`plan_and_review_documents()` 经 `_review_planner_output_with_retries()` 透传 `plan_documents()` 的 `{"run_id", "artifact_ids"}` 返回形状,含 `run_id`;`task.status` 新值确认是 `reviewing_planner_output`;`roles/planner_reviewer/role.json` 目前 `skills: ["planner-output-review", "unattended-agent-operations"]`,未预留 §7.1 新 skill 位置,Phase 2 实施时新增一行即可,不是不兼容)。 |
| 6 | 6 | **状态**: Phase 2a(commit `86fc0bb`)+ Phase 2b(commit `7c46a2f`)已实现,2026-08-12。Phase 2c 的 Validator 自规划改造(§6 核心部分)已实现,2026-08-12:`validate_implementation_with_own_graph()` 改为 `plan_work_graph(conn, "validator", ..., append_to_run_id=run_id)` 追加进 Planner/Coder 共享的同一个 `work_graph_run`,终审 verdict 变成图内一个真实节点(`node_type='final_verdict'`,`judgment.kind='mechanical'`、`check_ref='verdict_says_pass'`,`on_fail.action='dynamic_loop_back'`——Validator 图里唯一允许用这个 action 的节点),硬门槛(脚本真实非零退出码强制 FAIL,不听 LLM 辩解)通过 `_verdict_node_build_prompt`/`_verdict_says_pass_check` 的共享可变状态闭包保留,Python 强制而非只是 prompt 里说说。`_VALIDATOR_MANIFEST`/`_VALIDATOR_DOC_PROMPTS`/`_VALIDATOR_VERDICT_PROMPT`/`_validator_prompt_builder` 保留但不再使用(同 `_CODER_PROMPT` 先例)。§7.2 的收口验证已完成分析(见 §10 Phase 2c 小节末尾:一旦 verdict 节点自己的 `dynamic_loop_back` 内部消化重试,`validate_implementation_with_own_graph()` 正常返回时 verdict 必为 PASS,`org_level_loop.py` 的旧粗粒度路由对这条新路径自然不可达,不需要显式新旧流程判断——决定保留旧分支代码不删,已在 `scheduler.py::finish_task_lifecycle` 加注释说明)。 |
| 7 | 7 | |
| 8 | -**2026-08-13 §2.1/§2.2 已实施**:`work_graph_run.org_node` 列彻底删除,`ux_work_graph_run_active` 收紧为 `UNIQUE(task_id) WHERE status NOT IN ('done', 'failed')`('blocked' 故意算作活跃——恢复一个 blocked 的 run 现在总是走 `append_to_run_id` 追加进同一行,不会再 INSERT 新行)。新迁移函数 `schema.migrate_drop_work_graph_run_org_node()`(内部先幂等调用 `migrate_relax_org_node_constraints()`,再做 create-copy-drop-rename,`PRAGMA foreign_keys=OFF` 全程包裹,理由同 `migrate_add_reviewing_planner_output_status` 的真实重启事故记录——`work_graph_run` 是被多张表 REFERENCES 的父表)已接入 `backend_server.py::_ensure_schema()`。下游改动:`work_graph_plan.py`/`work_graph_fork.py`(含 `create_fork()`/`_reconstruct_build_prompt()`/`execute_fork()`,`fork_info["org_node"]` 改名 `fork_info["subagent_type"]`,源节点自己的执行身份)/`root_cause_trace.py` 的三处 INSERT 全部去掉 `org_node` 列;`work_graph_run_query.py`(`list_work_graph_runs`/`get_work_graph_run`)与 `task_query.py::get_task` 的返回形状从单值 `"org_node"` 改成 `"org_nodes_involved"`(从 `work_node_execution.subagent_type` 去重派生的列表,`org_node=` 查询参数语义从等值匹配改成 EXISTS 子查询),前端 `frontend/app.js` 对应改造(`getLatestRunForOrgNode`/`orgNodesLabel`/`renderOrgGraphTopology` 等消费点全部改用列表语义)。发现一处设计文档本节 §2.1.3 没预料到的遗漏点:`task_query.py::get_task` 也有同样的单值 `org_node` 读取,一并修完。`root_cause_trace.py::judge_root_cause_via_work_graph` 的 INSERT 有一个已知、记录在代码注释里的边界情况:索引收紧后,若这个函数被调到时对应 task 已有一个仍处于活跃/blocked 状态的共享 run,会撞索引——目前不是真实风险(这个函数唯一的调用方 `org_level_loop.run_fix_loop` 只在一个已经不可达的分支里被调用,且它自己的测试夹具里对应 task 从未预先插入过 work_graph_run 行),但如果未来重新接上这条路径需要重新评估。`python3 -m unittest discover -s src`(768 tests)+ `discover -s supervisor`(126 tests)+ 4 个 frontend smoke test 全绿。**仍未实施**:§7.1(新 skill `work-graph-node-review` + 三个 role.json 追加)。**2026-08-08 修订**:写作过程中,需求文档 §7 的四个开放问题已被用户直接拍板(详见该文档 §7 对应条目),其中两条推翻了本文档最初的设计判断——Coder/Validator 自规划改为"建议节点清单"而非完全自由(§3),`work_graph_run.org_node` 改为彻底移除而非保留(§2.1)——本版本已按拍板结果改写,不是原始草稿。**2026-08-08 追加**(同日,见需求文档新增 §3.1):FR-4 的目标被明确为"失败即进化"完整方法论(判断失败 → Reflection-review 动态诊断 → 系统性缺陷则先 Fix-Skill → 跳回执行),当时 §5 设计的 `dynamic_loop_back` 只覆盖第 1/2/4 步,第 3 步(Fix-Skill)是已知缺口。**2026-08-12 补齐**:§5 已扩写为完整四步设计(§5.4 Reflection-review 现在同时判断目标节点和缺陷性质,§5.5 新增 Fix-Skill,真实调用既有但从未真正投入使用的 `skill_update.py::update_skill()`),过程中发现两个既有事实(`skills:` frontmatter 只按名字动态加载内容、不需要重新部署;`org_level_loop.py` 的 `_ROUTE_FOR_CATEGORY` 从来没有 `skill_defect` 这个 key,`update_skill()` 在这个代码库里投产以来从未被真正调用过)——详见 §5 开头。由哪个身份执笔改 Skill 已通过 `AskUserQuestion` 确认(2026-08-12):统一用 Validator,见 §5.5。 | |
| 8 | +**2026-08-13 §2.1/§2.2 已实施**:`work_graph_run.org_node` 列彻底删除,`ux_work_graph_run_active` 收紧为 `UNIQUE(task_id) WHERE status NOT IN ('done', 'failed')`('blocked' 故意算作活跃——恢复一个 blocked 的 run 现在总是走 `append_to_run_id` 追加进同一行,不会再 INSERT 新行)。新迁移函数 `schema.migrate_drop_work_graph_run_org_node()`(内部先幂等调用 `migrate_relax_org_node_constraints()`,再做 create-copy-drop-rename,`PRAGMA foreign_keys=OFF` 全程包裹,理由同 `migrate_add_reviewing_planner_output_status` 的真实重启事故记录——`work_graph_run` 是被多张表 REFERENCES 的父表)已接入 `backend_server.py::_ensure_schema()`。下游改动:`work_graph_plan.py`/`work_graph_fork.py`(含 `create_fork()`/`_reconstruct_build_prompt()`/`execute_fork()`,`fork_info["org_node"]` 改名 `fork_info["subagent_type"]`,源节点自己的执行身份)/`root_cause_trace.py` 的三处 INSERT 全部去掉 `org_node` 列;`work_graph_run_query.py`(`list_work_graph_runs`/`get_work_graph_run`)与 `task_query.py::get_task` 的返回形状从单值 `"org_node"` 改成 `"org_nodes_involved"`(从 `work_node_execution.subagent_type` 去重派生的列表,`org_node=` 查询参数语义从等值匹配改成 EXISTS 子查询),前端 `frontend/app.js` 对应改造(`getLatestRunForOrgNode`/`orgNodesLabel`/`renderOrgGraphTopology` 等消费点全部改用列表语义)。发现一处设计文档本节 §2.1.3 没预料到的遗漏点:`task_query.py::get_task` 也有同样的单值 `org_node` 读取,一并修完。`root_cause_trace.py::judge_root_cause_via_work_graph` 的 INSERT 有一个已知、记录在代码注释里的边界情况:索引收紧后,若这个函数被调到时对应 task 已有一个仍处于活跃/blocked 状态的共享 run,会撞索引——目前不是真实风险(这个函数唯一的调用方 `org_level_loop.run_fix_loop` 只在一个已经不可达的分支里被调用,且它自己的测试夹具里对应 task 从未预先插入过 work_graph_run 行),但如果未来重新接上这条路径需要重新评估。`python3 -m unittest discover -s src`(768 tests)+ `discover -s supervisor`(126 tests)+ 4 个 frontend smoke test 全绿。 | |
| 9 | + | |
| 10 | +**2026-08-13 §7.1 已实施**:新增 `skills/work-graph-node-review/SKILL.md`——判断"自规划 Work Graph 节点(Coder/Validator/planner_reviewer 自己规划出的、声明 `judgment.kind="llm"` 的节点)的产出是不是真的、结构化的",不是叙述文字冒充产出;吸收了 `docs/todo/validator-test-script-narrative-output-issue.md` §6 的根因分析和 `_NARRATION_RETRY_HINT`(条件句而非断言句)的措辞教训,并明确跟 `planner-output-review`(Planner 固定文档链、任务级、Coder 开始前把关)、`qa-task-validation`(任务级整体验收)、`skill-defect-remediation`(改 Skill 文件本身)三者的边界——判断粒度是"这一个自规划节点",不是文档链/整任务/Skill 文件。已挂到 `roles/planner_reviewer`、`roles/coder`、`roles/validator` 三个 `role.json` 的 `skills` 列表(各追加一行,如 §9 核对清单当时预判)。未修改 `.claude/agents/*.md`——那是 `agent_profile.deploy_agent_definitions()` 从 `role.json` 编译出的部署产物,由 Deploy Supervisor 在真实 cutover 时重新生成,不是手动跟 `role.json` 同步维护的(观察到现有 `.claude/agents/coder.md` 已经缺了更早前 Phase 2a 加的 `design-dynamic-work-graph`,印证这个文件本来就不是手动保持同步的)。`python3 -m unittest discover -s src`(768 tests,含 `test_agent_profile.py`)全绿。 | |
| 11 | + | |
| 12 | +**Phase 2c 至此全部完成**(§2.1/§2.2/§6 Validator 自规划改造/§7.1 均已实施;§7.2 收口分析已完成,决定不改代码,见上文)。**2026-08-08 修订**:写作过程中,需求文档 §7 的四个开放问题已被用户直接拍板(详见该文档 §7 对应条目),其中两条推翻了本文档最初的设计判断——Coder/Validator 自规划改为"建议节点清单"而非完全自由(§3),`work_graph_run.org_node` 改为彻底移除而非保留(§2.1)——本版本已按拍板结果改写,不是原始草稿。**2026-08-08 追加**(同日,见需求文档新增 §3.1):FR-4 的目标被明确为"失败即进化"完整方法论(判断失败 → Reflection-review 动态诊断 → 系统性缺陷则先 Fix-Skill → 跳回执行),当时 §5 设计的 `dynamic_loop_back` 只覆盖第 1/2/4 步,第 3 步(Fix-Skill)是已知缺口。**2026-08-12 补齐**:§5 已扩写为完整四步设计(§5.4 Reflection-review 现在同时判断目标节点和缺陷性质,§5.5 新增 Fix-Skill,真实调用既有但从未真正投入使用的 `skill_update.py::update_skill()`),过程中发现两个既有事实(`skills:` frontmatter 只按名字动态加载内容、不需要重新部署;`org_level_loop.py` 的 `_ROUTE_FOR_CATEGORY` 从来没有 `skill_defect` 这个 key,`update_skill()` 在这个代码库里投产以来从未被真正调用过)——详见 §5 开头。由哪个身份执笔改 Skill 已通过 `AskUserQuestion` 确认(2026-08-12):统一用 Validator,见 §5.5。 | |
| 9 | 13 | |
| 10 | 14 | --- |
| 11 | 15 | ... | ... |
roles/coder/role.json
roles/planner_reviewer/role.json
| ... | ... | @@ -8,7 +8,7 @@ |
| 8 | 8 | }, |
| 9 | 9 | "context": { "memory": "project" }, |
| 10 | 10 | "model": "claude-sonnet-5", |
| 11 | - "skills": ["planner-output-review", "unattended-agent-operations"], | |
| 11 | + "skills": ["planner-output-review", "work-graph-node-review", "unattended-agent-operations"], | |
| 12 | 12 | "applications": { "mcp_servers": [] }, |
| 13 | 13 | "workflows": { "dynamic_planning": "none" }, |
| 14 | 14 | "permissions": { | ... | ... |
roles/validator/role.json
skills/work-graph-node-review/SKILL.md
0 → 100644
| 1 | +--- | |
| 2 | +name: work-graph-node-review | |
| 3 | +description: | | |
| 4 | + 给任何自规划 Work Graph 节点(Coder/Validator/planner_reviewer 自己规划出的、声明 | |
| 5 | + judgment.kind="llm" 的节点)判断"这一个节点的产出是不是真的、结构化的、完成了它被要求做的 | |
| 6 | + 事",而不是叙述文字/进度汇报冒充产出。这是节点级判断,不是任务级验收——判断粒度是"这一个 | |
| 7 | + 节点",不是"整个任务"。 | |
| 8 | +version: 1.0.0 | |
| 9 | +tags: | |
| 10 | + - review | |
| 11 | + - judgment | |
| 12 | + - work-graph | |
| 13 | + - self-planning | |
| 14 | +--- | |
| 15 | + | |
| 16 | +# 自规划节点产出复核 | |
| 17 | + | |
| 18 | +## 我在做什么、不做什么 | |
| 19 | + | |
| 20 | +我判断的是 Work Graph 里**一个具体节点**的产出是否合格——这个节点是 Coder/Validator/ | |
| 21 | +planner_reviewer 自己规划出来的(`task_prompt` 字段描述它该做什么),不是 Planner 固定文档链 | |
| 22 | +上的 brd/prd/sdd/benchmark(那是 `planner-output-review` 的职责,判断粒度是整批文档、发生在 | |
| 23 | +Coder 开始之前一次性把关);我也不判断"整个任务是否验收通过"(那是 `qa-task-validation` 的 | |
| 24 | +职责,任务级)。我的判断只回答一个问题:**这一个节点,针对它自己的 `task_prompt`,给出的 | |
| 25 | +`content` 是不是真的完成了要求的事**。 | |
| 26 | + | |
| 27 | +我是只读判断,不执行代码、不修改文件——如果节点本身需要机械验证(比如脚本是否真的能编译/运 | |
| 28 | +行),那用 `judgment.kind="mechanical"`,不是我;我处理的是 `judgment.kind="llm"` 的场景, | |
| 29 | +即"这份内容好不好"只能靠语义判断,没有客观对错的机械检查方式。 | |
| 30 | + | |
| 31 | +## 判断标准:是不是叙述文字冒充产出 | |
| 32 | + | |
| 33 | +**真实反复复现过的一类系统性失败**(`docs/todo/validator-test-script-narrative-output-issue.md` | |
| 34 | +§6 的完整根因记录):要求"先用工具探索、再产出最终内容"的节点,模型探索完之后的收尾话术天然 | |
| 35 | +倾向"汇报我刚才做了/启动了什么",而不是切换成"直接吐出最终内容"模式——即使节点自己的提示词 | |
| 36 | +已经写了"不要写叙述文字",这句话作为附加在提示词末尾的格式规则,不足以覆盖"多轮工具调用之后 | |
| 37 | +的收尾习惯"这个更底层的行为模式。真实复现过的原文片段: | |
| 38 | + | |
| 39 | +- "Launched the workflow in the background, run ID wf_.... I'll report results once it | |
| 40 | + completes; no further action is needed right now." | |
| 41 | +- "The workflow completed. It authored `test_....js` — an independent mechanical test for | |
| 42 | + ..."(描述"我做了什么",不是测试脚本本身的内容) | |
| 43 | + | |
| 44 | +这些文本的共同特征:**在描述"我刚才启动/完成了什么动作",而不是产出该动作本来应该产生的 | |
| 45 | +内容本身**。判断时把这个节点的 `task_prompt` 当作唯一的标尺——它要求的是代码、是测试输出、 | |
| 46 | +是结构化分析,产出就必须直接"是"那个东西,而不是关于"我如何得到那个东西"的说明。单独出现的 | |
| 47 | +词("workflow"、"launched")不构成判断依据本身——判断的是**整段内容的性质**,不要因为内容里 | |
| 48 | +合理提到了"运行"、"执行"这类词就误判。 | |
| 49 | + | |
| 50 | +## 定位到具体缺口,不要给笼统判断 | |
| 51 | + | |
| 52 | +`ok=false` 时,`issues` 里每一条都要能回答"内容里具体哪一部分不对/缺什么",不是"整体上不够 | |
| 53 | +好"这种不可操作的结论。好的判断形状是:"task_prompt 要求给出可运行的测试脚本,但 content 的 | |
| 54 | +开头三段全是进度汇报式散文,直到第四段才出现看起来像代码的内容,且缺少必要的 import"——具体 | |
| 55 | +到能让重新生成时明确知道该改什么。 | |
| 56 | + | |
| 57 | +## 措辞上的教训:用条件句,不用断言句 | |
| 58 | + | |
| 59 | +`_NARRATION_RETRY_HINT`(`src/e2e_pipeline.py`)在给重试反馈时刻意用"If your previous output | |
| 60 | +was a narrative description..."这种条件句,不是"Your output was a narrative description"这种 | |
| 61 | +断言句——因为这个判断本身有误判空间:一段内容也可能只是普通的语法笔误导致机械检查失败,不是 | |
| 62 | +真的叙述文字。判断时同样适用这个原则:如果不能确定内容性质就是叙述文字而不是别的问题(比如 | |
| 63 | +格式错误、遗漏字段),`issues` 里的措辞也应该描述"观察到的具体现象",而不是给出一个可能张冠 | |
| 64 | +李戴的定性指控。 | |
| 65 | + | |
| 66 | +## 跟 `_NODE_REVIEW_JUDGE_PROMPT` 的关系 | |
| 67 | + | |
| 68 | +`work_graph_plan.py::_NODE_REVIEW_JUDGE_PROMPT` 是这份方法论压缩成的、可以直接作为 | |
| 69 | +`judgment.judge_prompt` 使用的机械提示词常量,被 `_CODER_NODE_CATALOG` 的 `unit_test`/ | |
| 70 | +`integration_check` 条目引用为 `suggested_judgment` 默认值。这份 skill 不是重复那个常量,而是 | |
| 71 | +它背后"为什么这么判断、真实失败案例长什么样"的方法论沉淀——规划节点时如果需要自己写一个 | |
| 72 | +`judge_prompt`(而不是直接复用目录里的建议),应该参考这里的判断标准,而不是从零发明一套。 | |
| 73 | + | |
| 74 | +## 输出格式 | |
| 75 | + | |
| 76 | +`ok`(布尔)+ `issues`(字符串列表,`ok=false` 时每条都指向具体缺口)。不改写节点自己的 | |
| 77 | +`content`,不建议节点该怎么重新生成——那是 `on_fail.action` 触发的重试/回退机制的事,我只 | |
| 78 | +负责判断这一次的产出合不合格。 | ... | ... |