Commit 528ec6e1b8c9fedaccd32516671305983c8bee25

Authored by bboonnee
1 parent 0f8230e8

docs: complete Phase 2 Fix-Skill design (step 3 of the self-evolution methodology)

Design for org-graph-unification-and-universal-self-planning-requirements.md
§3.1's step 3, previously flagged as the largest known gap in the Phase 2 SDD.

Extends §5.4's Reflection-review to judge both a target node AND a
defect_kind (content_error vs skill_defect), adds §5.5 Fix-Skill (a real
Validator-identity call rewriting the target node's skill_ref content, via
the existing but never-wired-up skill_update.py::update_skill() +
bug_record.create_bug_record()), and confirms Re-loop needs no new
mechanism -- the existing _LoopBackRequested path already re-runs the
target node, which picks up the new skill content automatically since
Claude Code loads Skill content by name at call time, not at agent
frontmatter deploy time.

Two findings during design that motivated/validated this work:
- org_level_loop.py's _ROUTE_FOR_CATEGORY has no 'skill_defect' key --
  skill_update.update_skill() has never been called by any production
  path despite root_cause_trace.py being able to classify skill_defect
  since U-015. Fix-Skill is the first time this dead capability gets wired
  up for real, via a new path, not by fixing the old one.
- execution_event.event_type's CHECK enum has had an unused 'skill_iterated'
  value since the original U-001 schema -- no new migration needed to log
  Fix-Skill events.

Also found and recorded a third work_graph_run.org_node call site
(root_cause_trace.judge_root_cause_via_work_graph) that §2.1's column-
removal missed.

Confirmed via AskUserQuestion: the Skill-rewrite call always runs under
the Validator identity, regardless of which org_node's skill is being
fixed -- avoids the self-correcting-its-own-blind-spot problem, and
matches skill_update.py's own "a Validator-triggered rewrite" docstring
intent from 03-C.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
docs/todo/org-graph-unification-and-universal-self-planning-requirements.md
@@ -76,7 +76,7 @@ @@ -76,7 +76,7 @@
76 3. **Fix-skill 节点**:不是修这一次的输出,是**修 Skill 本身**——真实调用已经存在的 Skill 迭代机制(`src/skill_update.py::update_skill()` + `src/bug_record.py::create_bug_record(root_cause_category='skill_defect')`),产出一次真实的、Git 版本化、可回滚的 Skill 内容更新(`skill_iteration` 表,`before_git_commit_sha`/`after_git_commit_sha` 都是真实 commit,不是模拟)。**这套机制在代码库里早就存在**(U-015/U-016 时期就建好了),但至今只在 `org_level_loop.py` 的 Org 级粗粒度路径上被触发过——FR-4 要把它真正接进 Work Graph 内部的判断/回路循环,让它成为节点级失败恢复的常规路径之一,不是只有跨 Org Agent 的大失败才配触发。 76 3. **Fix-skill 节点**:不是修这一次的输出,是**修 Skill 本身**——真实调用已经存在的 Skill 迭代机制(`src/skill_update.py::update_skill()` + `src/bug_record.py::create_bug_record(root_cause_category='skill_defect')`),产出一次真实的、Git 版本化、可回滚的 Skill 内容更新(`skill_iteration` 表,`before_git_commit_sha`/`after_git_commit_sha` 都是真实 commit,不是模拟)。**这套机制在代码库里早就存在**(U-015/U-016 时期就建好了),但至今只在 `org_level_loop.py` 的 Org 级粗粒度路径上被触发过——FR-4 要把它真正接进 Work Graph 内部的判断/回路循环,让它成为节点级失败恢复的常规路径之一,不是只有跨 Org Agent 的大失败才配触发。
77 4. **跳回执行**:Skill 更新落地后,跳回"用这个 Skill 的节点"重新执行——这次用的是刚修好的新 Skill,不是拿旧 Skill 再赌一次运气。跟 `_loop_history_prompt_suffix`(现有机制,把历史反馈拼进 prompt)的本质区别:prompt 里的反馈是**这一次任务专属、跑完就忘**的临时纠偏;Skill 更新是**持久化、跨任务复用**的真正修复——两者不互斥(可以同时生效:这次重试既带着历史反馈,也带着已经更新的 Skill),但 FR-4 的核心价值在后者。 77 4. **跳回执行**:Skill 更新落地后,跳回"用这个 Skill 的节点"重新执行——这次用的是刚修好的新 Skill,不是拿旧 Skill 再赌一次运气。跟 `_loop_history_prompt_suffix`(现有机制,把历史反馈拼进 prompt)的本质区别:prompt 里的反馈是**这一次任务专属、跑完就忘**的临时纠偏;Skill 更新是**持久化、跨任务复用**的真正修复——两者不互斥(可以同时生效:这次重试既带着历史反馈,也带着已经更新的 Skill),但 FR-4 的核心价值在后者。
78 78
79 -**跟 Phase 2 SDD 的关系**:`docs/todo/org-graph-unification-phase2-universal-self-planning-design.md` §5 设计的 `on_fail.action="dynamic_loop_back"` 是这个方法论第 1/2/4 步(判断失败 → 动态诊断根因节点 → 跳回执行)的**已完成设计**,但**第 3 步(Fix-skill)还没有设计**——目前的 `dynamic_loop_back` 只处理"一次性内容错误"这一种诊断结果,没有覆盖"系统性 skill 缺陷"这条分支。这是 Phase 2 设计文档需要补齐的部分,不是另开一个 Phase——具体设计(Reflection-review 节点判断"一次性 vs 系统性"的标准、Fix-skill 节点的 graph_manifest 形状、"用哪个 Skill 文件"跟"节点声明的 skill_ref"字段(FR-3)怎么对应上)留给该文档的下一次修订。 79 +**跟 Phase 2 SDD 的关系**:`docs/todo/org-graph-unification-phase2-universal-self-planning-design.md` §5 已经是四步的完整设计(2026-08-12 补齐)——`on_fail.action="dynamic_loop_back"` 触发时,Reflection-review(§5.4)同时判断"跳回哪个节点"和"这是一次性错误还是系统性 Skill 缺陷";系统性缺陷时先经过 Fix-Skill(§5.5,真实调用既有但从未真正投入生产使用的 `skill_update.py::update_skill()`,产出 Git 版本化的 Skill 迭代)再跳回执行。由哪个身份执笔改 Skill,已确认(2026-08-12)统一用 Validator(该文档 §5.5)。
80 80
81 --- 81 ---
82 82
docs/todo/org-graph-unification-phase2-universal-self-planning-design.md
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 ****: 2026-08-08 3 ****: 2026-08-08
4 **扯捶**: 霂衣挽霈∴笆摨 `docs/todo/org-graph-unification-and-universal-self-planning-requirements.md` Phase 2嚗R-2/FR-3/FR-4嚗 禮4/禮4.1 撌脫嚗獢 A嚗楝敺挽霈~ 4 **扯捶**: 霂衣挽霈∴笆摨 `docs/todo/org-graph-unification-and-universal-self-planning-requirements.md` Phase 2嚗R-2/FR-3/FR-4嚗 禮4/禮4.1 撌脫嚗獢 A嚗楝敺挽霈~
5 **靘**: 蝖砌 Phase 1嚗docs/done/org-graph-unification-phase1-review-gate-design.md`嚗歇摰嚗ommit `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 雿蔭嚗hase 2 摰憓銵嚗銝摰對 5 **靘**: 蝖砌 Phase 1嚗docs/done/org-graph-unification-phase1-review-gate-design.md`嚗歇摰嚗ommit `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 雿蔭嚗hase 2 摰憓銵嚗銝摰對
6 -****: 霈曇恣hase 1 蔭隞嗅歇皛∟雲嚗隞亙憪**2026-08-08 靽株恥**嚗葉嚗瘙﹝ 禮7 葵撘憸歇鋡怎嚗祕閫砲﹝ 禮7 撖孵嚗銝凋舅蝧颱﹝挽霈∪oder/Validator 閫銝"撱箄悅皜"嚗3嚗work_graph_run.org_node` 銝箏蝠摨宏2.1嚗撌脫蝏阮**2026-08-08 餈賢**嚗嚗瘙﹝憓 禮3.1嚗R-4 ◤&銝"憭梯揖餈"摰瘜捏嚗憭梯揖 Reflection-review 蝟餌抒撩 Fix-Skill 頝喳銵﹝ 禮5 霈曇恣 `dynamic_loop_back` 閬銝剔洵 1/2/4 甇伐洵 3 甇伐ix-Skill嚗撌脩蝻箏嚗 禮5 撘憭渲秩 禮8 蝚 5 6 +****: 霈曇恣hase 1 蔭隞嗅歇皛∟雲嚗隞亙憪**2026-08-08 靽株恥**嚗葉嚗瘙﹝ 禮7 葵撘憸歇鋡怎嚗祕閫砲﹝ 禮7 撖孵嚗銝凋舅蝧颱﹝挽霈∪oder/Validator 閫銝"撱箄悅皜"嚗3嚗work_graph_run.org_node` 銝箏蝠摨宏2.1嚗撌脫蝏阮**2026-08-08 餈賢**嚗嚗瘙﹝憓 禮3.1嚗R-4 ◤&銝"憭梯揖餈"摰瘜捏嚗憭梯揖 Reflection-review 蝟餌抒撩 Fix-Skill 頝喳銵 禮5 霈曇恣 `dynamic_loop_back` 閬洵 1/2/4 甇伐洵 3 甇伐ix-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
7 7
8 --- 8 ---
9 9
@@ -87,6 +87,10 @@ if org_node: @@ -87,6 +87,10 @@ if org_node:
87 87
88 3. **`INSERT INTO work_graph_run (...)`嚗洵 164-168 銵****`fork_info`/`execute_fork()` `org_node`嚗洵 189/208 銵**嚗NSERT 霂剖 `org_node` execute_fork()` 靚 `run_graph()` 隡 `fork_info["org_node"]` 蔭嚗隡 `fork_info["subagent_type"]`嚗撌梁銵澈隞踝蛹 `run_graph()` 蝑曉"瘝⊥蝥扯恕"嚗 禮4.1嚗 88 3. **`INSERT INTO work_graph_run (...)`嚗洵 164-168 銵****`fork_info`/`execute_fork()` `org_node`嚗洵 189/208 銵**嚗NSERT 霂剖 `org_node` execute_fork()` 靚 `run_graph()` 隡 `fork_info["org_node"]` 蔭嚗隡 `fork_info["subagent_type"]`嚗撌梁銵澈隞踝蛹 `run_graph()` 蝑曉"瘝⊥蝥扯恕"嚗 禮4.1嚗
89 89
  90 +#### 2.1.3 `root_cause_trace.py::judge_root_cause_via_work_graph`嚗洵銝挽霈 禮5 Fix-Skill 嚗2.1 阮憭
  91 +
  92 +`judge_root_cause_via_work_graph()`嚗root_cause_trace.py:222-226`嚗撌曹甈 `INSERT INTO work_graph_run (run_id, task_id, org_node, graph_manifest, status) VALUES (...)` Planner/Coder/Validator 銝 `org_node` 閬葵葵頨思敶勗餅銝粹摰∪撱箔撘蝡嚗蕭楊畾萄之嚗 `work_graph_run` 銵函 INSERT 霂剖敹◆頝嚗瞍﹝ 禮5.4 `_judge_root_cause_node` 憭餈葵爸嚗隡挾隞◇韏瑟
  93 +
90 ### 2.2 `ux_work_graph_run_active` 霂凋閬宏嚗 94 ### 2.2 `ux_work_graph_run_active` 霂凋閬宏嚗
91 95
92 `schema.py:151-152`嚗CREATE UNIQUE INDEX ux_work_graph_run_active ON work_graph_run(task_id, org_node)`嚗 status嚗祗銋" task org_node 銝芣暑頝 run"銝 task 蝔銝芣暑頝 run嚗 org_node 銝蝝W漲閬 `(task_id, org_node)` 蝝找蛹 `(task_id)`嚗 96 `schema.py:151-152`嚗CREATE UNIQUE INDEX ux_work_graph_run_active ON work_graph_run(task_id, org_node)`嚗 status嚗祗銋" task org_node 銝芣暑頝 run"銝 task 蝔銝芣暑頝 run嚗 org_node 銝蝝W漲閬 `(task_id, org_node)` 蝝找蛹 `(task_id)`嚗
@@ -220,15 +224,19 @@ def _run_one_node(conn, run_id, org_node, skill_id, target_folder_path, node, ou @@ -220,15 +224,19 @@ def _run_one_node(conn, run_id, org_node, skill_id, target_folder_path, node, ou
220 224
221 --- 225 ---
222 226
223 -## 5. 憓 `on_fail.action = "dynamic_loop_back"`嚗笆摨 禮1.3嚗R-4 敹 227 +## 5. 憓 `on_fail.action = "dynamic_loop_back"` + 摰郊頝荔eflection-review Fix-Skill Re-loop嚗
  228 +
  229 +瘙﹝ 禮3.1嚗"憭梯揖餈"嚗 FR-4 摰蛹郊嚗憭梯揖 Reflection-review 嚗甈⊥批捆秤 vs 蝟餌 Skill 蝻粹嚗憒蝟餌抒撩嚗耨 Skill 頨 頝喳銵 2026-08-08 擐霈曇恣鈭嚗洵 3 甇伐ix-Skill嚗甈∟‘朣甈∟挽霈∟葉霂颱誨鈭舅銝芰摰 Fix-Skill 霂交秩
224 230
225 -**2026-08-08 霂湔**嚗瘙﹝憓 禮3.1嚗"憭梯揖餈eflection Fix-Skill Re-loop"嚗 FR-4 瘜捏摰蛹郊憭梯揖eflection-review **憒蝟餌 Skill 蝻粹,耨 Skill 頨恬 `skill_update.py::update_skill()`,鈭批 Git Skill 餈凋誨)**歲銵挽霈∠ `dynamic_loop_back` 閬洵 1/2/4 甇伐 + 頝喳銵笆摨"銝甈⊥批捆秤"餈蝘蝏**"蝟餌 skill 蝻粹"嚗洵 3 甇 Fix-skill 嚗瓷挽霈**嚗﹝撌脩撩嚗甈∩耨霈g雿圾嚗eflection-review "銝甈⊥ vs 蝟餌"ix-skill graph_manifest 敶Yskill_update.update_skill()` 閬 `skill_id`/`repo_path` 頝 禮4 蝥 `skill_ref` 摮挾笆摨 231 +** A嚗嚗摰" Skill 隡"嚗**嚗.claude/agents/<org_node>.md` `skills:` frontmatter 撘 Skill ****嚗agent_profile.py::_frontmatter_yaml`嚗迤 `skills/<skill_id>/SKILL.md` 捆 Claude Code 撌勗瘥活 `claude --agent <org_node>` 靚蝸**摰 Skill 辣漱嚗甈∟銝 org_node 撠曹銝捆嚗閬蝵 `.claude/agents/*.md`**" Skill 頝喳銵"餈葵頝臭閬甇"蝵"嚗
  232 +
  233 +** B嚗秩餈葵隞挽嚗**嚗org_level_loop.py::run_fix_loop()` `_ROUTE_FOR_CATEGORY = {"coder": "fast_lane", "planning": "full_replan"}`嚗org_level_loop.py:10`嚗**瘝⊥ `skill_defect` 餈葵 key**root_cause_trace.py` 撠梯鈭批 `skill_defect` 餈葵蝐餃嚗_PERSPECTIVE_FRAMINGS["skill_defect"]`嚗root_cause_trace.py:112-116`嚗bug_record.create_bug_record()` 銋葵蝐餃嚗 `route = _ROUTE_FOR_CATEGORY.get(category, "full_replan")` 撖 `skill_defect` `full_replan` 餈葵潑**`skill_update.py::update_skill()` 餈葵隞漣隞交隞鋡思遙雿漣隞楝敺迤靚餈**嚗ix-Skill 蝚砌甈⊥撠梯挽霈∪末瓷蝥輻迤韏瑟葵 dead-code 蝻箏頨怨 Phase 2 嚗蝡靽殷 禮8 蝚 6 甈∟挽霈∠憛★嚗ix-Skill 韏啁頝臬耨憟質葵 bug
226 234
227 ### 5.1 霂凋 235 ### 5.1 霂凋
228 236
229 -頝 `loop_back`嚗歲銝芾畾萄停香摰dynamic_loop_back` 銵函內"餈葵憭梯揖嚗銝甈∩靚嚗葵銝虜蟡迤霂亙"鈭**蝡舐恣批**嚗 Phase 1 撘恣 Validator 嚗葵餈葵韐萇 237 +頝 `loop_back`嚗歲銝芾畾萄停香摰dynamic_loop_back` 銵函內"餈葵憭梯揖嚗銝甈∩靚嚗eflection-review嚗葵銝虜蟡迤霂亙僎餈銝甈⊥批捆秤餈蝟餌 Skill 蝻粹嚗耨 Skill嚗ix-Skill嚗"鈭**蝡舐恣批**嚗 Phase 1 撘恣 Validator 嚗葵餈葵韐萇
230 238
231 -### 5.2 graph_manifest 摮挾 239 +### 5.2 graph_manifest 摮挾嚗
232 240
233 ```jsonc 241 ```jsonc
234 { 242 {
@@ -239,49 +247,137 @@ def _run_one_node(conn, run_id, org_node, skill_id, target_folder_path, node, ou @@ -239,49 +247,137 @@ def _run_one_node(conn, run_id, org_node, skill_id, target_folder_path, node, ou
239 } 247 }
240 ``` 248 ```
241 249
242 -### 5.3 銵餉work_graph_orchestrate.py` 憓嚗  
243 -  
244 -`_run_one_node()` `action = (node.get("on_fail") or {}).get("action", "retry_self")` 憓嚗 250 +### 5.3 銵餉餉work_graph_orchestrate.py` 憓嚗
245 251
246 ```python 252 ```python
247 if action == "dynamic_loop_back": 253 if action == "dynamic_loop_back":
248 ancestors = sorted(_transitive_ancestors(graph_manifest, node_id) - {node_id}) 254 ancestors = sorted(_transitive_ancestors(graph_manifest, node_id) - {node_id})
249 - diagnosis = _judge_root_cause_node(  
250 - conn, node_id, feedback, ancestors, org_node, target_folder_path  
251 - ) # 閫 禮5.4嚗 255 + diagnosis = _judge_root_cause_node( # 閫 禮5.4嚗eflection-review
  256 + conn, run_id, node_id, feedback, ancestors, graph_manifest, build_prompt, outputs,
  257 + org_node, target_folder_path,
  258 + )
252 target = diagnosis["target_node_id"] 259 target = diagnosis["target_node_id"]
253 - if target not in ancestors: # 餈敺∴"摰撉" 260 + if target not in ancestors: # 餈敺∴ 禮5.7
254 raise ValueError( 261 raise ValueError(
255 "dynamic_loop_back judge picked {!r}, not a real ancestor of {}".format(target, node_id) 262 "dynamic_loop_back judge picked {!r}, not a real ancestor of {}".format(target, node_id)
256 ) 263 )
  264 + if diagnosis["defect_kind"] == "skill_defect":
  265 + _fix_skill_for_node( # 閫 禮5.5嚗ix-Skill耨摰 Skill 圻
  266 + conn, run_id, target, graph_manifest, build_prompt, outputs, feedback, target_folder_path,
  267 + )
257 raise _LoopBackRequested(source_node_id=node_id, target_node_id=target, feedback=feedback) 268 raise _LoopBackRequested(source_node_id=node_id, target_node_id=target, feedback=feedback)
258 ``` 269 ```
259 270
260 -`_LoopBackRequested`/`run_graph()` 銝餃儐嚗_run_layers` `loop_back_signal` work_graph_orchestrate.py:811-830`嚗**摰銝閬**歇蝏"蝏銝 `(source_node_id, target_node_id)` 撠梢銵 target 銝虜"嚗敹葵 target ㄟ_transitive_dependents`/`_loop_history_prompt_suffix`/`_resolve_loop_limit` 憭**餈霂&摰"憓銝 action """**楝銵嚗"摰 target 靚"餈甇交 271 +`_LoopBackRequested`/`run_graph()` 銝餃儐嚗_run_layers` `loop_back_signal` work_graph_orchestrate.py:811-830`嚗**摰銝閬**歇蝏"蝏銝 `(source_node_id, target_node_id)` 撠梢銵 target 銝虜"嚗敹葵 target ㄟ敹歲瓷 Fix-Skill 餈甇乓target` 銵隡銝漱 Skill 捆嚗 A嚗**餈停蝚 4 甇"頝喳銵 Skill"摰嚗閬"雿輻 Skill"**嚗蛹 Skill 蝸撠望餈
261 272
262 -### 5.4 `_judge_root_cause_node`嚗 `root_cause_trace.py` 摰∪蝏 273 +### 5.4 Reflection-review嚗_judge_root_cause_node` + 蝻粹扯捶
263 274
264 -`root_cause_trace.judge_root_cause_via_work_graph`嚗docs/done/verifier-fan-out-jury-design.md`嚗歇蝏"憭 Work Graph fan-out + `_tally_perspectives` 憭蟡其輔鋆"嚗隞予銝漲蝐餃嚗planning`/`coder`/`skill_defect`嚗憭銝芷爸嚗"蝐餃""雿"嚗 275 +憭 `root_cause_trace.judge_root_cause_via_work_graph` "憭 Work Graph fan-out + `_tally_perspectives` 憭蟡其輔鋆"撉冽嚗活閬摰∪蝏嚗**銝甈∟銝甈⊥折秤餈蝟餌抒撩** Reflection-review 頝臬敹提嚗""餈
265 276
266 ```python 277 ```python
267 -def _judge_root_cause_node(conn, failed_node_id, feedback, candidate_node_ids, org_node, target_folder_path, timeout=180):  
268 - """頝 root_cause_trace.judge_root_cause_via_work_graph 葵閫  
269 - 銝甈∠蝡 Work Node fan-out 靚嚗 candidate_node_ids  
270 - 銝迎嚗tally_perspectives 狡 >=2/3 憭蟡其輔鋆  
271 - < 2/3 銝銝 root_cause_trace InconclusiveEscalateError 漣  
272 - 鈭箏極 禮5.5嚗"餈虜"漣鈭箏極嚗  
273 - 蛹餈葵頨怠停隞亙銝頧桃甈 dynamic_loop_back 餈隡  
274 - 嚗掩漣鈭箏極蝏 278 +_REFLECTION_OUTPUT_SCHEMA = {
  279 + "type": "object", "required": ["target_node_id", "defect_kind", "detail"],
  280 + "properties": {
  281 + "target_node_id": {"type": "string"},
  282 + "defect_kind": {"type": "string", "enum": ["content_error", "skill_defect"]},
  283 + "detail": {"type": "string"},
  284 + },
  285 +}
  286 +
  287 +def _judge_root_cause_node(
  288 + conn, run_id, failed_node_id, feedback, candidate_node_ids, graph_manifest, build_prompt, outputs,
  289 + org_node, target_folder_path, timeout=180,
  290 +):
  291 + """銝葵閫葵銝甈∠蝡 Work Node fan-out 靚嚗
  292 + judge_root_cause_via_work_graph 憟嚗
  293 + {target_node_id, defect_kind, detail} 嚗arget_node_id
  294 + 敹◆ candidate_node_ids 銋蟡其輔鋆輕摨行 target_node_id嚗銝
  295 + 憭巨撠望銝迎efect_kind 砲蟡函
  296 + 嚗蟡券 target defect_kind 蝏撖寧巨嚗
  297 + < 2/3 銝銝漣鈭箏極嚗 root_cause_trace 銝 禮5.7 蝚 2 嚗
275 """ 298 """
276 ``` 299 ```
277 300
278 -銝葵閫遣霈殷"銝芾漣捆頨急捶蝻粹"嚗笆葵 output_payload嚗"憒瘝⊥捶蝻粹嚗銝銋/銵憸"摰銝甈∪ `feedback`嚗恣頨怎仃韐亙銝葵閫 `candidate_node_ids` 銝迎 `_tally_perspectives` 蟡券餉輔鋆 301 +銝葵閫雿嚗
  302 +
  303 +1. **捆閫**嚗"葵 `candidate_node_ids` 瘥葵餈活 `outputs` 漣捆嚗build_prompt(candidate_node, outputs)` 遣摰◤閬銋 + `outputs[candidate_node_id]` 摰銋銝銝芰捆頨怎餈活捆憟賣瓷末嚗content_error`嚗餈掩捆餈掩憸絲瘜捏/prompt 頨急頂蝏批榆嚗skill_defect`嚗" `validator-test-script-narrative-output-issue.md` 禮6 "蝝W漣"餈掩撠曇憭拍曉膩蛹 `skill_defect` 雿葵閫霈拙蝛箏
  304 +2. **銵閫**嚗"憒葵瘝⊥捶捆蝻粹嚗銝銋蝟/颲銵頨急憸銝芾霂乩銝銝芾瘝∪ㄟ `depends_on`嚗" 餈虜 `content_error`嚗活閫 Skill 瘜捏秤嚗
  305 +3. **閫**嚗"餈葵撖孵 org_node/skill_ref 蝏 `execution_event`嚗loop_back_triggered`/`skill_iterated` 銝斤掩撌脩蛹蝐駁憸◤頝喳耨餈 Skill嚗銝芸曉 `skill_defect`嚗蝚砌甈∟曉 `content_error`" 餈銝葵閫銝頝其遙/頝刻蔭甈∪銝迎閬銝甈 `execution_event`嚗甈∠ `outputs`
  306 +
  307 +### 5.5 Fix-Skill嚗耨 Skill嚗靽株甈∠
  308 +
  309 +**閫血隞**嚗_judge_root_cause_node` 摰 `defect_kind == "skill_defect"`**閬耨銝 Skill**嚗 `graph_manifest` `target_node_id` 撌勗ㄟ `skill_ref`嚗4 蝥抒挾嚗撩蝥折恕 `skill_id`嚗 `work_node_execution` 銵剁撘” `node_id` 撣 `-attemptN` 嚗 `graph_manifest` 摰粉郁銋
  310 +
  311 +**靚靽栽歇嚗2026-08-12嚗 Validator 頨思遢**嚗org_node="validator"`嚗恣憸撌望銝 org_node嚗銝悟摰撌望撌梁 Skill嚗skill_update.py` 璅∪撌梁 docstring "a **Validator**-triggered rewrite" 03-C 停摰挽霈⊥嚗霈曇恣蝚砌甈∠迤韏瑟嚗韐函嚗銝 org_node Skill 頨急頂蝏批榆嚗悟摰銝迎榆靽株撌梁 Skill嚗之璁耨銝摮活頦拍摰撌梁嚗蝡 Validator 頨思遢餈"銝"餈葵 Reflection-review 銝
  312 +
  313 +****嚗
  314 +
  315 +```python
  316 +def _fix_skill_for_node(conn, run_id, target_node_id, graph_manifest, build_prompt, outputs, feedback, target_folder_path, timeout=300):
  317 + target_node = next(n for n in graph_manifest["nodes"] if n["node_id"] == target_node_id)
  318 + skill_id = target_node.get("skill_ref", "unknown") # 蝥折恕靚 run_graph() skill_id
  319 +
  320 + current_content = _read_skill_content(skill_id) # skills/<skill_id>/SKILL.md, skill_query._REPO_ROOT
  321 + task_id = conn.execute("SELECT task_id FROM work_graph_run WHERE run_id = ?", (run_id,)).fetchone()[0]
  322 + goal_text = conn.execute("SELECT goal_text FROM task WHERE task_id = ?", (task_id,)).fetchone()[0]
  323 +
  324 + rewrite_result = org_node_trigger.trigger_org_node_structured( # 閫 work-graph-node-execution-deblackbox-requirements.md
  325 + "validator", _SKILL_REWRITE_PROMPT.format(
  326 + goal_text=goal_text, skill_id=skill_id, current_content=current_content,
  327 + what_was_asked=build_prompt(target_node, outputs),
  328 + what_it_produced=outputs[target_node_id].get("content", ""),
  329 + failure_feedback="; ".join(feedback or []),
  330 + loop_history=_loop_history_prompt_suffix(conn, run_id, target_node_id), # 憭
  331 + ),
  332 + target_folder_path, _SKILL_REWRITE_SCHEMA, timeout=timeout,
  333 + )
  334 +
  335 + bug_result = bug_record.create_bug_record(
  336 + conn, task_id=task_id,
  337 + description="Skill {} produced repeated defective output at node {}".format(skill_id, target_node_id),
  338 + root_cause_category="skill_defect", root_cause_detail=rewrite_result["change_summary"],
  339 + suggested_route="full_replan", # 瘝輻蜀澆‵頝臬楝
  340 + )
  341 + iteration = skill_update.update_skill(
  342 + conn, skill_id, bug_result["bug_id"], rewrite_result["new_content"],
  343 + rewrite_result["change_summary"], repo_path=skill_query._REPO_ROOT, # 閫 A 銝
  344 + )
  345 + _log_skill_iterated_event(conn, run_id, target_node_id, skill_id, iteration["iteration_id"]) # 閫
  346 + return iteration
  347 +```
  348 +
  349 +`_SKILL_REWRITE_PROMPT`/`_SKILL_REWRITE_SCHEMA`嚗{"new_content": str, "change_summary": str}`嚗憓捆嚗瘜捏嚗"銝葵 Skill 迤蝖株蛹移蝖桐耨甇紡餈活憭梯揖銝撠"嚗 skill `skills/skill-defect-remediation/SKILL.md`嚗 禮7.1 嚗 `roles/validator/role.json` Validator 頨思遢銝 禮7.1 `work-graph-node-review` 提嚗銝芸"銝銝芾憟賭末"嚗葵"銝隞 Skill ﹝頨"嚗扯捶銝
279 350
280 -### 5.5 摰颲寧 351 +**霈啣甈 `skill_iterated` execution_event嚗嚗葵 event_type 撌脩 CHECK 蜀嚗 U-001 韏瑕停鋡思遙雿誨閬 schema 餈宏嚗**嚗
  352 +
  353 +```python
  354 +def _log_skill_iterated_event(conn, run_id, target_node_id, skill_id, iteration_id):
  355 + row = conn.execute("SELECT wgr.task_id, t.project_id FROM work_graph_run wgr "
  356 + "JOIN task t ON wgr.task_id = t.task_id WHERE wgr.run_id = ?", (run_id,)).fetchone()
  357 + conn.execute(
  358 + "INSERT INTO execution_event (event_id, project_id, task_id, run_id, event_type, payload) "
  359 + "VALUES (?, ?, ?, ?, 'skill_iterated', ?)",
  360 + (str(uuid.uuid4()), row[1], row[0], run_id, json.dumps({
  361 + "node_id": target_node_id, "skill_id": skill_id, "iteration_id": iteration_id,
  362 + })),
  363 + )
  364 + conn.commit()
  365 +```
  366 +
  367 +餈 event Phase 3 垢嚗R-5b 嚗"餈頧桐蛹隞銋頧桐"霈脫"頝喳"嚗"頝喳kill 鋡怎餈甈∴鈭銋"
  368 +
  369 +### 5.6 Re-loop嚗閬
  370 +
  371 +`_fix_skill_for_node` 餈5.3 蝝扳撣 `raise _LoopBackRequested(...)`"銝甈⊥批捆秤"韏啁銵誨舅頝臬銝撠望閬 禮5.5嚗歲銵頨怠憭嚗5.3 撌脩秩蛹隞銋閬 `run_graph()` 銝餃儐嚗
  372 +
  373 +### 5.7 摰颲寧憓 Fix-Skill 銝嚗
281 374
282 1. **餈撉◆**嚗5.3 撌脣內嚗雿踹靚頨怎銝芯 node_id嚗漣甈⊥楝 375 1. **餈撉◆**嚗5.3 撌脣內嚗雿踹靚頨怎銝芯 node_id嚗漣甈⊥楝
283 -2. **颲暹 2/3 銝**嚗 `root_cause_trace` 漣鈭箏極嚗頝 Org Agent 颲寧嚗銝甈⊿蛹" `candidate_node_ids` 氖憭梯揖餈銝虜"嚗_transitive_ancestors` 蝏 `depends_on` 鈭餈蟡銝芯餉敺餈恕潘甇餅銝芸儐  
284 -3. **`_resolve_loop_limit` 恣蝏游漲銝**嚗_loop_attempt_count_from_events` `(target_node_id, source_node_id)` 餈撖寡恣work_graph_orchestrate.py:560-567`嚗dynamic_loop_back` 瘥活銝 `target_node_id`嚗"銝 source 餈賒 3 甈∪ 3 銝芯 target"銝◤恣餉雿霈⊥ 1嚗**餈銝銝芷閬撘**嚗銝"銝 source_node_id 蝝航恣閫血 dynamic_loop_back 餅活"銝 target嚗蔭瘝輻 `work_graph_loop_limit_config` 撘”嚗config_id` `"{org_node}:{source_node_id}:total"` 餈葵 key 敶Y嚗甇"瘥活銝"蝏儐 376 +2. **颲暹 2/3 銝**嚗 `root_cause_trace` 漣鈭箏極嚗頝 Org Agent 颲寧嚗銝甈⊿蛹" `candidate_node_ids` 氖憭梯揖餈銝虜 + `defect_kind` 閫蛹 `content_error`"嚗恕&摰銝蝟餌折憸嚗ouch Skill嚗嚗
  377 +3. **`_resolve_loop_limit` 恣蝏游漲銝**嚗_loop_attempt_count_from_events` `(target_node_id, source_node_id)` 餈撖寡恣work_graph_orchestrate.py:560-567`嚗dynamic_loop_back` 瘥活銝 `target_node_id`嚗"銝 source 餈賒 3 甈∪ 3 銝芯 target"銝◤恣餉雿銝"銝 source_node_id 蝝航恣閫血 dynamic_loop_back 餅活"銝蔭瘝輻 `work_graph_loop_limit_config` 撘”嚗config_id` `"{org_node}:{source_node_id}:total"` 餈葵 key 敶Y嚗甇Y儐
  378 +4. ~~ Validator 頨思遢蝚 Skill 餈葵霈曇恣閬蝖株恕~~歇 `AskUserQuestion` 蝖株恕嚗2026-08-12嚗 Validator 頨思遢嚗 禮5.5
  379 +5. **銝 Skill 銝甈 run ◤**嚗_fix_skill_for_node` 頨急瓷"餈葵 skill_id 餈活 run 撌脩餈甈" Reflection-review 餈賒蔭摰銝 `skill_ref` 蝟餌抒撩嚗洵銝甈⊥敺憸憭嚗笆銝 Skill 辣餈賒鈭抒活 Git commit秤嚗活捏銝澆 禮5.3 "銝 `(run_id, skill_id)` 蝏甈 run Fix-Skill 閫血活"霈⊥嚗銝芸 2 甈∴雿踹摰 `skill_defect` 銋 `content_error` 憭甈∩遙隞 Skill雿潛畾萸
  380 +6. **self-target 嚗kill 摨ommit 遙撌梁誨鈭怠銝芰**skill_query._REPO_ROOT` 暺恕蝑 `paths.repo_root()`嚗 `target_folder_path` 憟賭 graphlemon 撌梧paths.is_self_target(target_folder_path)` 銝箇嚗舅**銝芯**update_skill()` 撖 `skill_query._REPO_ROOT` `git add`+`commit`嚗 05 禮5.8 憟 worktree 氖**蛹銋**嚗skill_update.py` 璅∪ docstring 撠梯秩"a Validator-triggered rewrite takes effect immediately"嚗kill 摨撠梯挽霈⊥銝颱遙撌梁誨閬氖僎舅蝒蝣 `skills/<skill_id>/` 遙撌望楝敺 `src/`嚗瓷瘚"self-target 隞餃銵嚗ix-Skill commit 砲隞餃撌梁 worktree 僎 git 撅遙雿閫漱鈭"遣霈 Phase 2c 摰撉撠甈∟葵蝏頝挽"捏銝蝒"撠梁"摰瓷鈭"嚗葵隞 worktree 銝摰 CLAUDE.md Isolation 蝡
285 381
286 --- 382 ---
287 383
@@ -379,6 +475,10 @@ Phase 1 SDD 霈曇恣 `plan_and_review_documents()`嚗憓嚗lanner @@ -379,6 +475,10 @@ Phase 1 SDD 霈曇恣 `plan_and_review_documents()`嚗憓嚗lanner
379 475
380 銝 `qa-task-validation`銝 skill "雿撉"嚗遙蝥改提銝舅僎摮僎 476 銝 `qa-task-validation`銝 skill "雿撉"嚗遙蝥改提銝舅僎摮僎
381 477
  478 +### 7.1.1 skill嚗skill-defect-remediation`嚗ix-Skill 銝5.5 憓
  479 +
  480 +憓 `skills/skill-defect-remediation/SKILL.md`嚗 `roles/validator/role.json`嚗 org-agent-蝚 Skill 澈隞賡閬 禮5.5 "靚靽"蝑瘜捏敹銝隞 Skill 迤蝖株蛹移蝖桀僎靽格迤撖潸餈活仃韐亦銝撠 `work-graph-node-review`嚗"銝銝芾憟賭末"嚗提摰銝葵 skill "銝隞賣瘜捏﹝頨"嚗僎餈隞 skill
  481 +
382 ### 7.2 `org_level_loop.py`/`org_loop_route.py` 漲頝舐隡 482 ### 7.2 `org_level_loop.py`/`org_loop_route.py` 漲頝舐隡
383 483
384 Phase 1 畾"憭憭梯揖 `org_level_loop.py` 蝎漲頝舐"嚗hase 1 SDD 腹獢 Phase 2 ◤嚗恣 Validator 憯唳 `on_fail.action="dynamic_loop_back"`嚗5嚗仃韐交**銝** `work_graph_run` 移蝖桀嚗閬 `route_org_loop()` 蝘" `task.status` `assigned_org_node`閫血銝銝芸"楊畾菔楝 484 Phase 1 畾"憭憭梯揖 `org_level_loop.py` 蝎漲頝舐"嚗hase 1 SDD 腹獢 Phase 2 ◤嚗恣 Validator 憯唳 `on_fail.action="dynamic_loop_back"`嚗5嚗仃韐交**銝** `work_graph_run` 移蝖桀嚗閬 `route_org_loop()` 蝘" `task.status` `assigned_org_node`閫血銝銝芸"楊畾菔楝
@@ -389,11 +489,12 @@ Phase 1 畾&quot;憭憭梯揖 `org_level_loop.py` 蝎漲頝舐 @@ -389,11 +489,12 @@ Phase 1 畾&quot;憭憭梯揖 `org_level_loop.py` 蝎漲頝舐
389 489
390 ## 8. &&摰改 Phase 1 憭改釣嚗 490 ## 8. &&摰改 Phase 1 憭改釣嚗
391 491
392 -1. **`_judge_root_cause_node`嚗5.4嚗韐券蝏**"銝芯虜霂亥提"霈曇恣,瘝⊥ A/B ,銝瑪之璁閬秤獢 prompt/閫挽霈,銝銝甈∪笆 492 +1. **`_judge_root_cause_node`嚗5.4嚗韐券蝏**"銝芯虜霂亥提 + 銝蝟餌抒撩"霈曇恣,瘝⊥ A/B ,銝瑪之璁閬秤獢 prompt/閫挽霈,銝銝甈∪笆defect_kind` 餈葵鈭撠文秤舅蝘誨隞瑚笆蝘堆頂蝏折憸秤甈⊥改 Skill嚗掩秤餈嚗誨隞瑕"瘝⊥憟"嚗甈⊥折憸秤頂蝏改甈 Skill 辣嚗誨隞瑟甈∩拿 Git 漱嚗澆瘜剁澆葉銝頝葵餈舅蝐餉秤
393 2. **`promotes_to_artifact`嚗2.3嚗 Coder/Validator 摰"蝏漱隞ㄟ"餈蝥行** prompt 撅蔓蝥行,銝璇啣撩,捏銝 LLM 蝏葵憯唳 `promotes_to_artifact`嚗 artifact_type 蜀頨思雿,雿雿"餈漲憯唳"餈誑嚗,閬葉閫閬璇唳 493 2. **`promotes_to_artifact`嚗2.3嚗 Coder/Validator 摰"蝏漱隞ㄟ"餈蝥行** prompt 撅蔓蝥行,銝璇啣撩,捏銝 LLM 蝏葵憯唳 `promotes_to_artifact`嚗 artifact_type 蜀頨思雿,雿雿"餈漲憯唳"餈誑嚗,閬葉閫閬璇唳
394 -3. **`dynamic_loop_back` " source 餅活銝"嚗5.5 蝚 3 嚗閬 `work_graph_loop_limit_config` key 敶Y**"{org_node}:{source_node_id}:total"` 餈葵雿撘瓷,摰閬餈 `settings_config.py` 霂/摰寡挾撘 key,餈閬撅 494 +3. **`dynamic_loop_back` " source 餅活銝"嚗5.7 蝚 3 嚗閬 `work_graph_loop_limit_config` key 敶Y**"{org_node}:{source_node_id}:total"` 餈葵雿撘瓷,摰閬餈 `settings_config.py` 霂/摰寡挾撘 key,餈閬撅
395 4. **`org_level_loop.py` 瘚摮靘**嚗7.2 蝏偏嚗瓷捏,畾菔圾憸,銝﹝葵憸圾,Phase 2 銝瑪敶予撠曹"隞餃隞楝敺",摰憿箏砲 495 4. **`org_level_loop.py` 瘚摮靘**嚗7.2 蝏偏嚗瓷捏,畾菔圾憸,銝﹝葵憸圾,Phase 2 銝瑪敶予撠曹"隞餃隞楝敺",摰憿箏砲
396 -5. **瘙﹝ 禮3.1 瘜捏洵 3 甇伐ix-skill嚗瘝⊥挽霈**嚗 禮5 撘憭游歇釣嚗dynamic_loop_back` 摰"霂 + 頝喳"嚗"蝟餌 Skill 蝻粹耨 Skill 頨"餈 FR-4 瘜捏&閬﹝餈瓷蝏雿挽霈∠敶憭抒蝻箏嚗蝏憸遣霈桐蛹 Phase 2c嚗蝡 Phase 2d嚗挽霈∴摰銝剝摰 496 +5. ~~Fix-Skill 靚蝚 Skill 閬蝖株恕~~歇蝖株恕嚗2026-08-12嚗 Validator 頨思遢嚗閫 禮5.5
  497 +6. **蝡 dead-code 蝻箏嚗憛 Phase 2 雿澆銝**嚗org_level_loop.py::_ROUTE_FOR_CATEGORY` 隞瘝⊥ `skill_defect` 餈葵 key嚗5 撘憭" B"嚗餈漲頝臬 `skill_update.update_skill()` 隞瘝∟◤迤靚餈hase 2 Fix-Skill 韏啁頝臬耨憟質葵 bug嚗 禮7.2 摰瘚摮畾菜嚗葵頝臬頨怎葵蝻箏摮瘣餌澆◇ `_ROUTE_FOR_CATEGORY` ‘銝 `"skill_defect": "full_replan"`嚗 `update_skill()`嚗霈抵楝銝恕 fallback嚗祗銋霂銝銝芰蝡遠隞仿耨憭 Phase 2c
397 498
398 --- 499 ---
399 500