diff --git a/pipeline_service/sdlc_ability.py b/pipeline_service/sdlc_ability.py index 6fd5793..15870ba 100644 --- a/pipeline_service/sdlc_ability.py +++ b/pipeline_service/sdlc_ability.py @@ -217,6 +217,11 @@ SDL_PROMPT = """你是「开发产线」的驾驶舱 agent,负责软件项目 - 除功能需求外,需求阶段还要明确部署环境需求:测试环境 + 生产环境的资源规格、依赖服务、端口、环境变量、高可用/备份/域名证书/监控告警等。 - 用户未提供时,在会话中逐步追问明确(用 ask_user / list_questions),不要跳过;明确后再创建 requirement 任务。 +## 迭代粒度(重要) +- 每个迭代有自己独立的初始任务(requirement 需求分析)和完整任务链(requirement → design → develop → deploy_test → test → deploy_prod)。 +- 创建 requirement 任务前,必须先 list_tasks 查「当前迭代」是否已有需求分析任务;同一迭代已有(含 approved/completed)→ 更新现有需求或推进下一阶段,禁止重复创建;只有新迭代(start_next_iteration)才需要新的初始任务。 +- 诊断卡点、派发任务、推进项目都以「当前迭代」为单位(diagnose_project 已按当前迭代统计),不要混着整个项目看。 + ## 典型场景(帮助判断何时用哪个工具,非强制) - 用户提出新的开发需求/功能("实现XX""XX系统要做XX")→ 先确认部署环境需求,再用 create_task 创建任务(可按 requirement→design→develop 拆分),再 start_agents 启动。**单个任务不要过大**:涉及多个模块/多个独立交付单元时按模块拆小(每个模块一个 develop 任务),禁止一次性派发「实现全部 N 个模块/全部表契约/脚手架+DDL 全套」这种巨型任务。**模块化原则**:模块划分+模块间依赖关系由 design 阶段设计师完成(需求阶段只识别应用/部署单元,PM 按设计师模块清单派发);apppublic/sqlor/ahserver/accounting/appbase/rbac 等基础模块已存在可直接引用,不必再开发。 - 用户问进展/状态 → list_tasks / diagnose_project / check_progress。 @@ -554,10 +559,18 @@ async def _h_diagnose_project(sor, p, ctx): if not pid: return "请先切换到项目" + # 迭代级诊断:诊断单位是「当前迭代」(in_progress),而非整个项目。 + # 项目只是迭代的容器;PM 诊断卡点、派发任务、推进都以当前迭代为单位。 + from .iteration_capability import get_current_iteration + cur_iter = await get_current_iteration(sor, pid) + iter_name = cur_iter.get('iteration_name', '') if cur_iter else '' + iter_cond = " AND JSON_UNQUOTE(JSON_EXTRACT(params,'$.iteration_id'))=${nm}$ " if iter_name else "" + iter_bind = {"nm": iter_name} if iter_name else {} + async def _cnt(state): recs = await sor.sqlExe( - "SELECT COUNT(*) as c FROM pipeline_tasks WHERE tenant_id=${pid}$ AND state=${st}$", - {"pid": pid, "st": state}) + "SELECT COUNT(*) as c FROM pipeline_tasks WHERE tenant_id=${pid}$ AND state=${st}$" + iter_cond, + {"pid": pid, "st": state, **iter_bind}) return getattr(recs[0], "c", 0) if recs else 0 sc = await _cnt("submitted") @@ -575,7 +588,7 @@ async def _h_diagnose_project(sor, p, ctx): pass lines = [ - "项目诊断:", + f"项目诊断(当前迭代:{iter_name or '无 in_progress 迭代(回退项目级)'}):", f"- 待执行: {sc}", f"- 运行中: {rc}", f"- 待审核: {vc}", @@ -585,15 +598,15 @@ async def _h_diagnose_project(sor, p, ctx): if fc: failed_rows = await sor.sqlExe( - "SELECT id, title, role FROM pipeline_tasks WHERE tenant_id=${pid}$ AND state='failed' ORDER BY created_at DESC LIMIT 10", - {"pid": pid}) + "SELECT id, title, role FROM pipeline_tasks WHERE tenant_id=${pid}$ AND state='failed'" + iter_cond + "ORDER BY created_at DESC LIMIT 10", + {"pid": pid, **iter_bind}) lines.append("失败任务:") for fr in (failed_rows or []): lines.append(f" - [{getattr(fr, 'role', '?')}] {getattr(fr, 'title', '')} (id={getattr(fr, 'id', '')})") if sc: submitted_rows = await sor.sqlExe( - "SELECT id, title, role FROM pipeline_tasks WHERE tenant_id=${pid}$ AND state='submitted' ORDER BY created_at ASC LIMIT 10", - {"pid": pid}) + "SELECT id, title, role FROM pipeline_tasks WHERE tenant_id=${pid}$ AND state='submitted'" + iter_cond + "ORDER BY created_at ASC LIMIT 10", + {"pid": pid, **iter_bind}) lines.append("待执行任务:") for sr in (submitted_rows or []): lines.append(f" - [{getattr(sr, 'role', '?')}] {getattr(sr, 'title', '')} (id={getattr(sr, 'id', '')})") @@ -601,9 +614,8 @@ async def _h_diagnose_project(sor, p, ctx): running_rows = await sor.sqlExe( "SELECT id, title, role, claimed_by, " "TIMESTAMPDIFF(MINUTE, updated_at, NOW()) AS mins " - "FROM pipeline_tasks WHERE tenant_id=${pid}$ AND state='running' " - "ORDER BY updated_at ASC LIMIT 10", - {"pid": pid}) + "FROM pipeline_tasks WHERE tenant_id=${pid}$ AND state='running'" + iter_cond + "ORDER BY updated_at ASC LIMIT 10", + {"pid": pid, **iter_bind}) lines.append("运行中任务:") for rr in (running_rows or []): rrole = getattr(rr, 'role', '?')