From 8eedff7af9dd5f5c95094b865b52b052bad349de Mon Sep 17 00:00:00 2001 From: ymq Date: Fri, 21 Aug 2026 23:39:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(sdlc):=20approve=5Ftask/complete=5Ftask=20?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=20PM=20=E8=A1=8C=E4=B8=BA=E6=8E=A8=E8=BF=9B?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E9=93=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - approve_task: 解决 pending review_reject + git 提交 + _get_next_role→迭代边界→_create_next_task 推进链 - complete_task: 同步交付件 review_status 标记 approved,避免任务 completed 但交付件仍 pending 不一致 - 会话 agent 手动 approve 不再断链(下一角色任务照常自动创建) --- pipeline_service/sdlc_ability.py | 65 +++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/pipeline_service/sdlc_ability.py b/pipeline_service/sdlc_ability.py index bc2cab3..cc116d4 100644 --- a/pipeline_service/sdlc_ability.py +++ b/pipeline_service/sdlc_ability.py @@ -550,7 +550,12 @@ async def _h_reset_task_retry(sor, p, ctx): async def _h_approve_task(sor, p, ctx): - """审核通过任务(PM 验收):review → approved。""" + """审核通过任务(PM 验收):review → approved + 对齐 PM 行为推进任务链。 + + 对齐 pm_review_run 的 approved 分支:解决该任务 pending 的 review_reject 问题、 + git 提交产出、查下一角色并创建下一阶段任务(含迭代边界检查)。这样会话 agent + 手动 approve 不会让任务链断(下一角色任务照常自动创建)。 + """ task_id = (p.get("task_id") or "").strip() if not task_id: return "需要任务ID" @@ -560,15 +565,61 @@ async def _h_approve_task(sor, p, ctx): full = await _resolve_task_id(sor, task_id, pid) if not full: return f"任务不存在: {task_id}" + recs = await sor.R('pipeline_tasks', {'id': full}) + if not recs: + return f"任务不存在: {task_id}" + task = recs[0] + task_role = getattr(task, 'role', '') or '' + title = getattr(task, 'title', '') or '' + comment = (p.get("comment") or "").strip() + from .task_capability import approve_task ok, msg = await approve_task(full, pid, who="agent.pm", - agent_id=ctx.get("user_id", ""), - comment=(p.get("comment") or "").strip()) - return f"OK: {msg}" if ok else f"ERROR: {msg}" + agent_id=ctx.get("user_id", ""), comment=comment) + if not ok: + return f"ERROR: {msg}" + + # 解决该任务 pending 的 review_reject 问题(对齐 PM,防永久堆积) + await sor.sqlExe( + "UPDATE pipeline_agent_questions SET status='answered', answer=${a}$, " + "answer_source='role_agent', answered_by=${role}$ " + "WHERE task_id=${tid}$ AND status='pending' " + "AND (problem_type='review_reject' " + " OR ((problem_type IS NULL OR problem_type='') AND from_role IN ('pm','cockpit')))", + {"a": "角色已响应,审核通过", "role": task_role or "role_agent", "tid": full}) + + # git 提交产出(对齐 PM:审核通过后统一提交;workspace_dir 空或 git 失败不阻断 approve) + workspace_dir = ctx.get("workspace_dir", "") or "" + if workspace_dir: + try: + from .agent_loop import _commit_repos_after_approve + await _commit_repos_after_approve(workspace_dir, title) + except Exception: + pass + + # 推进任务链(对齐 PM):查下一角色 + 迭代边界 + 创建下一阶段任务 + from .agent_loop import _get_next_role, _create_next_task, _task_iteration_name + next_role = await _get_next_role(task_role, pid) + if next_role: + task_iter = _task_iteration_name(task) + if task_iter: + _it = await sor.sqlExe( + "SELECT status FROM sd_iterations WHERE project_id=${pid}$ AND iteration_name=${name}$", + {"pid": pid, "name": task_iter}) + _it_status = getattr(_it[0], 'status', '') if _it else '' + if _it_status in ('completed', 'cancelled'): + return f"OK: {msg}(任务归属迭代「{task_iter}」已 {_it_status},链终止)" + next_tid, next_title = await _create_next_task(sor, pid, task, next_role, comment) + return f"OK: {msg};已创建下一阶段任务「{next_title}」({next_role},id={next_tid})" + return f"OK: {msg}(无下一角色,任务链终结)" async def _h_complete_task(sor, p, ctx): - """标记任务完成(PM):approved/review → completed。""" + """标记任务完成(PM):approved/review → completed + 同步交付件标记 approved(对齐 PM)。 + + 对齐 pm_review_run 的 review_complete 分支:先把交付件 review_status 从 pending 改 approved, + 避免任务 state=completed 但交付件 review_status 仍 pending 的状态不一致。 + """ task_id = (p.get("task_id") or "").strip() if not task_id: return "需要任务ID" @@ -578,6 +629,10 @@ async def _h_complete_task(sor, p, ctx): full = await _resolve_task_id(sor, task_id, pid) if not full: return f"任务不存在: {task_id}" + await sor.sqlExe( + "UPDATE pipeline_deliverables SET review_status='approved', review_comment=${cm}$ " + "WHERE task_id=${tid}$ AND review_status='pending'", + {"cm": (p.get("comment") or "").strip(), "tid": full}) from .task_capability import complete_task ok, msg = await complete_task(full, pid, who="agent.pm", agent_id=ctx.get("user_id", ""))