From ef20ceb6efa1a520056f7b07082a912557719ea6 Mon Sep 17 00:00:00 2001 From: yibo Date: Wed, 2 Sep 2026 10:54:06 +0800 Subject: [PATCH] fix(graph-engine): reopenRunForRetry/孤儿清扫重挂 auto 驱动循环 (T-mtjh8sdt) --- server/services/graph-engine.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+), 0 deletions(-) diff --git a/server/services/graph-engine.js b/server/services/graph-engine.js index f7cdfaf..69a06a0 100644 --- a/server/services/graph-engine.js +++ b/server/services/graph-engine.js @@ -3204,6 +3204,7 @@ function stopWallclockWatchdog() { */ function _reapStaleRunningNodes(rows, summary) { const swept = []; + const runIdsToRedrive = new Set(); for (const row of rows) { try { const run = getRun(row.run_id); @@ -3220,10 +3221,26 @@ function _reapStaleRunningNodes(rows, summary) { const out = { activated: [], backwardEdgeId: null, loop: null, contractDiff: null, crossLayer: null, reset: [], infraRerun: false }; routeFailure(row.run_id, def, nodeRun, out, null); swept.push({ runId: row.run_id, nodeRunId: row.id, infraRerun: out.infraRerun }); + runIdsToRedrive.add(row.run_id); } catch (err) { console.error(`[graph-engine] _reapStaleRunningNodes node=${row.id} 异常: ${err.message}`); } } + // T-mtjh8sdt 同一个洞的第二个入口:routeFailure 把节点重置回 ready/running 只改了状态, + // 不会重新挂上 runAuto 的自驱动循环(reopenRunForRetry 那次已经补过人工重试路径,这里 + // 是开机孤儿清扫 sweepOrphanedRunningNodes 与 45 号文档持续性存活扫描 + // sweepStaleRunningAgentCalls 共用的结算内层循环,同一缺陷的另一条触发路径)。 + // 异步补、不 await:sweepOrphanedRunningNodes 在服务器启动路径上同步调用,扫描可能 + // 命中好几个 run,不能让重新驱动的 agent 调用顶着启动耗时走;按 run 去重,避免同一个 + // run 里多个孤儿节点各自触发一次 runAuto。 + for (const runId of runIdsToRedrive) { + const after = getRun(runId); + if (after && after.run_status === 'running' && after.execution_mode === 'auto') { + runAuto(runId).catch((err) => { + console.error(`[graph-engine] 孤儿清扫重挂 auto 驱动异常 run=${runId}: ${err.message}`); + }); + } + } return { scanned: rows.length, swept }; } @@ -3802,6 +3819,13 @@ function descendantRuns(run) { * * @returns {void} 没有「失败」这个出口:要么推进了,要么被别人抢先推走了(都可以继续动节点), * 要么抛 RunSlotBusyError(整条重试作废,节点一个字都没动)。 + * + * **auto 模式重挂驱动循环**(T-mtjh8sdt):这里把 run 从 blocked/ready 拉回 running 走的是 + * 裸 `admitOrRequeue`,不是 `startRun`——所以以前只改了状态,从没重新挂上 `runAuto` 的自驱动 + * 循环。手动 `nextStep` 推一步后没人接着推,agentRunId 仍是 null,backward 重试每次都会在 + * 这里再卡一次。`startRun` 的收尾那段(判 execution_mode==='auto' 才挂 driver)搬过来一份: + * 只在真的把 run 推成了 running 且 execution_mode 是 auto 时才挂,manual 模式或没推动(requeued/ + * lost)都不挂,语义与 startRun 对齐。 */ function reopenRunForRetry(runId, reopenRun) { if (!reopenRun) return; @@ -3813,6 +3837,12 @@ function reopenRunForRetry(runId, reopenRun) { if (cur === 'blocked' || cur === 'ready') { admitOrRequeue(runId, cur, 'running', { summary: `人工重试重开链路:${cur} → running` }); } + const after = getRun(runId); + if (after && after.run_status === 'running' && after.execution_mode === 'auto') { + runAuto(runId).catch((err) => { + console.error(`[graph-engine] 人工重试重挂 auto 驱动异常 run=${runId}: ${err.message}`); + }); + } } /** -- libgit2 0.21.4