fix(intelligent-eval): reject report until all sessions closed
All checks were successful
CI / test (push) Successful in 3m59s

t480 实测:评估 completed 但 completed_sessions=0(进度 0%)。根因
(diagnosing-bugs):submit_report 无条件 CAS executing→completed,不看会话
状态——worker 建会话未 close 时,analyst 仍可提交报告把评估标完成。

- lifecycle.submit_report 加校验:存在会话时须全部 close(completed),
  否则抛 IntelligentEvalTransitionError(409)——杜绝'评估完成但进度 0%'
- 无会话的评估仍可直接提交(analyst 正常路径不变)
回归测试(诊断闭环 Phase 1 反馈环先红后绿):
- +test_submit_report_rejects_when_sessions_not_closed(running 会话→409)
- +test_submit_report_allows_when_sessions_closed(全 close→200)
900 passed
This commit is contained in:
sinohqb 2026-08-18 15:30:51 +08:00
parent 8959e7ac42
commit 56d709c44f
2 changed files with 32 additions and 1 deletions

View File

@ -197,9 +197,15 @@ def cancel(session: Session, eval_id: str) -> IntelligentEval:
def submit_report(session: Session, eval_id: str, report: dict[str, Any]) -> IntelligentEval: def submit_report(session: Session, eval_id: str, report: dict[str, Any]) -> IntelligentEval:
"""OpenClaw 提交结构化报告executing → completed。 """OpenClaw 提交结构化报告executing → completed。
报告结构校验在路由层pydantic此处只负责落库与状态迁移 报告结构校验在路由层pydantic此处只负责落库与状态迁移提交前必须
满足存在会话时须全部 closecompleted否则拒绝杜绝"评估 completed
completed_sessions=0进度 0%"的不一致worker 建会话未 close 时,
analyst 不应提前把评估标完成
""" """
repo = IntelligentEvalRepository(session) repo = IntelligentEvalRepository(session)
sessions = IntelligentEvalSessionRepository(session).list_by_eval(eval_id)
if sessions and any(s.status != IntelligentEvalSessionStatus.COMPLETED for s in sessions):
raise IntelligentEvalTransitionError("会话未全部关闭,不能提交报告")
result = repo._submit_report_if_executing(eval_id, report) result = repo._submit_report_if_executing(eval_id, report)
return _resolve_write( return _resolve_write(
eval_id, eval_id,

View File

@ -537,6 +537,31 @@ class TestReport:
resp = await client.get(f"/api/intelligent-evals/{eval_id}/report") resp = await client.get(f"/api/intelligent-evals/{eval_id}/report")
assert resp.status_code == 404 assert resp.status_code == 404
async def test_submit_report_rejects_when_sessions_not_closed(self, client, seeded_db):
"""评估不应在会话未全部 close 时被标 completed否则进度 0% 但状态完成)。
复现 t480 真实问题worker 建会话但未 closeanalyst 仍提交报告
submit_report 无条件 executingcompletedcompleted_sessions=0
"""
eval_id = await _create_executing_eval(client)
await _create_session(client, eval_id) # 建 1 个 running 会话,不 close
resp = await client.put(f"/api/intelligent-evals/{eval_id}/report", json={"report": _report_payload()})
# 会话未全部 close → 应拒绝409而非 200 直接 completed
assert resp.status_code == 409, resp.text
async def test_submit_report_allows_when_sessions_closed(self, client, seeded_db):
"""会话全部 close 后提交报告应成功(正常 analyst 路径不被破坏)。"""
eval_id = await _create_executing_eval(client)
session = await _create_session(client, eval_id)
resp = await client.post(
f"/api/intelligent-evals/{eval_id}/sessions/{session['id']}/close",
json={"verdict": {"goal_achieved": True, "summary": "完成", "severity": "low"}},
)
assert resp.status_code == 200, resp.text
resp = await client.put(f"/api/intelligent-evals/{eval_id}/report", json={"report": _report_payload()})
assert resp.status_code == 200, resp.text
assert resp.json()["status"] == "completed"
async def test_markdown_before_submission_returns_404(self, client, seeded_db): async def test_markdown_before_submission_returns_404(self, client, seeded_db):
eval_id = await _create_executing_eval(client) eval_id = await _create_executing_eval(client)
resp = await client.get(f"/api/intelligent-evals/{eval_id}/report/markdown") resp = await client.get(f"/api/intelligent-evals/{eval_id}/report/markdown")