diff --git a/pipeline_service/agent_loop.py b/pipeline_service/agent_loop.py index a04151b..6c76a3d 100644 --- a/pipeline_service/agent_loop.py +++ b/pipeline_service/agent_loop.py @@ -480,6 +480,12 @@ __REPO_STATE__ PM_SYSTEM_PROMPT = """你是项目经理(PM)。你的职责是项目计划、任务分配、任务验收。 +## 项目推进铁律(最高优先级) +- 默认必须推进项目执行:审核通过后自动创建后续任务、自动分解派发、推动项目往前走,**无需用户或会话 agent 的指令**。 +- **不必询问**用户或会话 agent「是否继续 / 是否推进」——自动推进是默认行为。 +- 自动推进无需指令;**暂停推进才需要指令**。仅当用户明确指令「暂停 / 停止推进」时,项目才会被置为 paused 状态(由会话 agent 调 pause_project),此时你才停止推进。 +- 你审核时若项目已处于 paused 状态,系统会直接跳过、不派发给你;恢复推进(resume_project)后自动继续。 + ## 三大职责 1. 项目计划:基于需求/设计交付件,规划后续工作——先评估任务复杂度,复杂任务自动分解为可执行的子任务。 2. 任务分配:用 create_tasks 工具把子任务派发给对应角色 agent,并记录父子关系与依赖关系(能并行的并行、不能并行的串行)。 @@ -1367,6 +1373,12 @@ async def pm_review_run(project_id, agent_id=None, model_name=None): async with db.sqlorContext("pipeline") as sor: # PM 审核用产线 default_model(无角色专属模型),org_id 多租户隔离 model_name, org_id = await _resolve_llm_context(sor, project_id, 'pm', model_name) + # 暂停门控:项目 paused 时 PM 不推进(不认领审核任务)。默认必须推进,暂停才需要指令。 + _precs = await sor.sqlExe( + "SELECT status FROM sd_projects WHERE id=${pid}$", {"pid": project_id}) + await sor.sqlExe("COMMIT", {}) + if _precs and getattr(_precs[0], 'status', '') == 'paused': + return {"status": "idle", "message": "项目已暂停推进"} task = await _claim_task(sor, project_id, '', state=TASK_REVIEW, match_role=False, set_state='review') if not task: return {"status": "idle", "message": "没有待审核任务"} diff --git a/pipeline_service/project_capability.py b/pipeline_service/project_capability.py index aa7293a..08b9575 100644 --- a/pipeline_service/project_capability.py +++ b/pipeline_service/project_capability.py @@ -31,6 +31,7 @@ TABLE = "sd_projects" # 项目状态(SDLC 默认,状态机语义见 project skill) S_DRAFT = "draft" # 草稿(已创建,未启动) S_ACTIVE = "active" # 进行中 +S_PAUSED = "paused" # 已暂停(暂停推进,PM 不再推进项目,除非恢复) S_COMPLETED = "completed" # 已完成 S_ARCHIVED = "archived" # 已归档 @@ -135,6 +136,19 @@ async def reopen_project(project_id, who=None, agent_id=None): who=who, agent_id=agent_id) +async def pause_project(project_id, who=None, agent_id=None): + """暂停推进:active → paused。暂停后 PM 不再推进项目(不认领审核任务、不创建后续任务), + 直到 resume_project 恢复。仅用户明确指令「暂停推进」时才调用——默认必须推进,无需指令。""" + return await _transition(project_id, S_ACTIVE, S_PAUSED, 'pause', + who=who, agent_id=agent_id) + + +async def resume_project(project_id, who=None, agent_id=None): + """恢复推进:paused → active。""" + return await _transition(project_id, S_PAUSED, S_ACTIVE, 'resume', + who=who, agent_id=agent_id) + + async def set_project_state(project_id, from_state, to_state, who=None, agent_id=None, detail=None): """通用 CAS 状态迁移兜底(跨产线自定义状态机用)。""" diff --git a/pipeline_service/sdlc_ability.py b/pipeline_service/sdlc_ability.py index 3fa2415..c03b13c 100644 --- a/pipeline_service/sdlc_ability.py +++ b/pipeline_service/sdlc_ability.py @@ -158,6 +158,8 @@ SDL_TOOLS = [ ToolDefinition(name="complete_project", description="完成项目(active→completed)", parameters={"project_id": "项目ID"}, category="project"), ToolDefinition(name="archive_project", description="归档项目(completed→archived)", parameters={"project_id": "项目ID"}, category="project"), ToolDefinition(name="reopen_project", description="重新打开归档项目(archived→active)", parameters={"project_id": "项目ID"}, category="project"), + ToolDefinition(name="pause_project", description="暂停项目推进(active→paused,PM 不再推进,仅用户明确指令暂停时用)", parameters={"project_id": "项目ID"}, category="project"), + ToolDefinition(name="resume_project", description="恢复项目推进(paused→active)", parameters={"project_id": "项目ID"}, category="project"), ToolDefinition(name="backup_project", description="归档备份项目(打包工作目录+数据记录为tgz到机构工作目录_archive/)", parameters={"project_id": "项目ID"}, category="project"), ToolDefinition(name="delete_project", description="删除项目(需二次确认confirm=true,删除前自动归档备份tgz)", parameters={"project_id": "项目ID", "confirm": "二次确认,必须为true"}, category="project"), # ── 迭代 ── @@ -217,7 +219,7 @@ SDL_PROMPT = """你是「开发产线」的驾驶舱 agent,负责软件项目 - 发现卡点(任务卡死、审核超时、失败、僵尸 claimed_by)时,主动定位根因并推动修复。 - 发现「待回答问题」> 0 或角色提问时,加载 team-communication 技能按冒泡链处理(list_questions 列问题 → 能答则 answer_question → 不能答 escalate_question 沿冒泡路径转下一个处理方)。 - 功能/需求管理 → 用户提新需求时 propose_feature;查功能清单 list_features;需求评审/验收时 approve_feature / reject_feature / verify_feature(状态机规范见 feature 技能)。 -- 项目管理 → list_projects 列项目;启动/完成/归档/重开项目用 start_project / complete_project / archive_project / reopen_project(状态机规范见 project 技能)。 +- 项目管理 → list_projects 列项目;启动/完成/归档/重开项目用 start_project / complete_project / archive_project / reopen_project;暂停/恢复推进用 pause_project / resume_project(仅用户明确指令「暂停推进」时才 pause,默认必须推进,状态机规范见 project 技能)。 - 迭代管理 → create_iteration 建迭代、list_iterations 列迭代;开始/完成/取消迭代用 start_iteration / complete_iteration / cancel_iteration(规范见 iteration 技能)。 - 交付件评审 → submit_deliverable 提交、list_deliverables 列交付件;评审用 approve_deliverable / reject_deliverable(规范见 deliverable 技能)。 - 测试计划 → create_test_plan 建计划、list_plans 列计划;审批/执行/完成用 approve_plan / start_plan / complete_plan(规范见 test-plan 技能)。 @@ -973,6 +975,26 @@ async def _h_reopen_project(sor, p, ctx): return f"OK: {msg}" if ok else f"ERROR: {msg}" +async def _h_pause_project(sor, p, ctx): + pid = (p.get("project_id", "") or "").strip() + if not pid: + return "需要项目ID" + pid = await _resolve_id(sor, "sd_projects", pid) or pid + from .project_capability import pause_project + ok, msg = await pause_project(pid, who="agent.main_agent") + return f"OK: {msg}" if ok else f"ERROR: {msg}" + + +async def _h_resume_project(sor, p, ctx): + pid = (p.get("project_id", "") or "").strip() + if not pid: + return "需要项目ID" + pid = await _resolve_id(sor, "sd_projects", pid) or pid + from .project_capability import resume_project + ok, msg = await resume_project(pid, who="agent.main_agent") + return f"OK: {msg}" if ok else f"ERROR: {msg}" + + async def _h_backup_project(sor, p, ctx): pid = (p.get("project_id", "") or "").strip() if not pid: @@ -1498,6 +1520,8 @@ SDL_HANDLERS = { "complete_project": _h_complete_project, "archive_project": _h_archive_project, "reopen_project": _h_reopen_project, + "pause_project": _h_pause_project, + "resume_project": _h_resume_project, "backup_project": _h_backup_project, "delete_project": _h_delete_project, # ── 迭代 ──