diff --git a/server/services/work-graph-db.js b/server/services/work-graph-db.js index 11814b9..903dba0 100644 --- a/server/services/work-graph-db.js +++ b/server/services/work-graph-db.js @@ -2024,7 +2024,7 @@ function settleTaskFromRun(taskId, runId, status) { const now = new Date().toISOString(); return getDb() .prepare(` - UPDATE tasks SET status = @status, finished_at = @now, updated_at = @now + UPDATE tasks SET status = @status, blocked_reason = NULL, finished_at = @now, updated_at = @now WHERE id = @id AND current_run_id = @runId AND status NOT IN (${TASK_TERMINAL_STATUS.map((s) => `'${s}'`).join(', ')}) diff --git a/server/tests/unit/goal-task-acceptance.test.js b/server/tests/unit/goal-task-acceptance.test.js index 97e5151..6935292 100644 --- a/server/tests/unit/goal-task-acceptance.test.js +++ b/server/tests/unit/goal-task-acceptance.test.js @@ -304,6 +304,9 @@ describe('27-P3C 批 1 验收(§八 4 / 5 / 6 / 10 / 11 / 12b)', () => { expect(sse.transitionRun(body.runId, 'running', 'blocked', {}, { summary: '用例模拟预算耗尽' })).toBeTruthy(); // task 行要看得见「卡住了」—— 这是界面上唯一的信号 expect(wdb.getTask(body.taskId).status).toBe('blocked'); + // 界面上那句「卡在哪」的小字(真实链路里由 describeRunBlock + noteTaskBlockedReason 写) + expect(wdb.noteTaskBlockedReason(body.taskId, body.runId, '节点 xxx 卡住(budget_exhausted)')).toBe(1); + expect(wdb.getTask(body.taskId).blocked_reason).toBeTruthy(); // 但 goal 不许收敛:挂起等人加预算 ≠ 需求失败。判 failed 会写 OKR、唤醒上级、 // 并把 goal 锁进终态,人加完预算跑完也再改不回 done(缺陷 2:误报 + 白烧一轮 LLM) expect(wdb.getGoal(body.goalId).status).toBe('active'); @@ -323,6 +326,11 @@ describe('27-P3C 批 1 验收(§八 4 / 5 / 6 / 10 / 11 / 12b)', () => { const task = wdb.getTask(body.taskId); expect(task.status).toBe('completed'); // 修之前这里永远钉死在 blocked expect(task.finished_at).toBeTruthy(); + // 33 号文档同批修的另一个真实 bug:settleTaskFromRun 只写 status,不清 blocked_reason, + // 于是任务列表在「已完成」绿色状态旁边,还会一直挂着上一次卡住时留下的那句红字 + // ——真实回归任务 T-mt2r6fz0-125b 复现过(org-coder 恢复跑完后,界面仍显示 + // 「节点 org-coder 卡住(missing_required_input)」)。 + expect(task.blocked_reason).toBeNull(); expect(wdb.getGoal(body.goalId).status).toBe('done'); expect(okrCalls).toEqual([{ nodeId: 'O-002.1', status: 'done' }]);