-
## 1. 子 run 转终态不回写父节点 → 任务永久卡死(界面走查发现) 复现:org 节点的子 work run 第一次失败 → 父节点判 fail → 入口节点没有 fail 出边 → blocked → 用户在中栏把子 run 修好跑到 completed → **父节点仍钉在 blocked, root run 也钉住,org 层永远推不下去**。唯一人工出路是对 org 节点点「重试」, 而那会把整张子图重置到下一个 attempt,刚跑完的成果全丢。 根因:propagateToParent 的幂等守卫写的是 `parentNode.status !== 'running' → return`, 判据用错了维度——它想防「同一个结果被结算两次」,实际拦掉的是「子 run 换了个结果」。 subgraph 节点的状态本来就是从子 run 派生的,子 run 换了终态就该重新派生。 修法:新增 reviveParentFromChild,只认「转好」(fail/blocked → success), 且要求 parentNode.child_run_id 正是这个子 run。恢复走 **blocked → ready → running → success 三步合法转换**,而不是给全局放宽 NODE_TRANSITIONS——转换表是给所有调用方的 安全网,不该为一条恢复路径把 blocked → success 对外开放;三条 node.status 事件 也让「这个节点是被救回来的」在 timeline 上看得见。 补测试时才暴露的第二半:父 run 多半也钉在 blocked,而 RUN_TRANSITIONS 没有 blocked → completed,settleRun 在转换非法时是**静默跳过**的。所以复活时要先把 父 run 从 blocked 拉回 running(这条合法),否则节点复活了 run 还是卡着。 ## 2. 边界守卫把并行 agent 的写算到当前节点头上(真 CLI 冒烟的断点) 真跑实测:招聘模板库里没有的角色走 hired_from_base,自动流水线起 agent-creator, 而它的 CLI workDir 就是 system/agents/agent-creator/——一个被 git 跟踪的目录。 它在里面重写自己的 todo.md、删一条 scheduled_tasks,被整仓 git diff 扫到, 归属算到当时正在跑的 work 节点,一个**真正成功**的节点被判 fail。 只要这些运行态文件还被 git 跟踪,每次含 hired_from_base 的招聘都稳定复现。 修法:splitUnattributable 把 agent 运行态文件(todo.md / plan.md / memory.md / _lessons.md / .claude/scheduled_tasks.json)从越界证据里摘出去,单独记一条 unattributable,不参与判罚。摘除范围刻意窄——同目录下的 agent.md / config.json 等源码仍然算越界。 路径提取按「首个空白之前」切而不是固定偏移:diffRepoStatus 已去掉前导空格, porcelain 状态位长度不定(修改类 "M path" 1 字符、未跟踪类 "?? path" 2 字符), 固定 slice(3) 恰好对**修改类**失效——而 agent-creator 改自己 todo.md 正是修改类。 这条单独立了一个回归用例。 ponytail: 这是缓解不是根治。根治要把运行态文件从 git 移出去(27-P3B W13), 本次只保证守卫不再冤枉节点;同一次招聘里 agent 若写了运行态文件之外的源码, 仍会算到当前节点头上。 测试 760/760 全绿(72 文件,起点 753/71):新增 graph-engine-revive-parent.test.js 4 条 + graph-boundary.test.js 3 条。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>