feat(sdlc): 判断粒度落到迭代——create_task 迭代级查重 + diagnose_project 迭代级诊断 + prompt 迭代约束

1. _h_create_task:创建 requirement 任务前查「当前迭代」是否已有非 cancelled 的
   requirement 任务(迭代的初始任务),有则拒绝重复创建,避免人事项目2 那种
   需求分析完成后又重复启动需求分析。
2. _h_diagnose_project:诊断单位从「项目」改为「当前迭代」(in_progress),
   按 iteration_id 过滤统计/列出任务,避免多迭代并存时诊断结果混在一起。
3. SDL_PROMPT:加「迭代粒度」约束——每个迭代独立初始任务+任务链,
   创建 requirement 前先查重、诊断/派发/推进都以当前迭代为单位。
This commit is contained in:
ymq 2026-08-21 13:04:58 +08:00
parent ca0cfc29de
commit 9c4f3deea6

View File

@ -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 创建任务可按 requirementdesigndevelop 拆分 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', '?')