diff --git a/src/daemon/toolchain_handler.py b/src/daemon/toolchain_handler.py index 1d1b58e..47deb42 100644 --- a/src/daemon/toolchain_handler.py +++ b/src/daemon/toolchain_handler.py @@ -297,22 +297,24 @@ class ToolchainHandler(BaseTaskHandler): logger.info("Toolchain %s: verify failed (%s), marked failed", task_id, verify.reason) - # 读取 must_haves 获取事件上下文 + # 读取 must_hives 获取事件上下文 + assignee 从 tasks 表读取 meta = {} + assignee = agent_id try: conn = get_connection(db_path) row = conn.execute( - "SELECT must_haves FROM tasks WHERE id=?", (task_id,) + "SELECT must_haves, assignee FROM tasks WHERE id=?", (task_id,) ).fetchone() - if row and row["must_haves"]: - meta = json.loads(row["must_haves"]) + if row: + if row["must_haves"]: + meta = json.loads(row["must_haves"]) + assignee = row["assignee"] or agent_id conn.close() except Exception: pass action_type = meta.get("action_type", "") context_data = meta.get("context", {}) - assignee = meta.get("assignee", "") or meta.get("from", "") # 三分路决策 route = self._classify_failure(verify) @@ -403,29 +405,43 @@ class ToolchainHandler(BaseTaskHandler): self, task_id: str, agent_id: str, verify: VerifyResult, db_path: Path, ) -> None: - """基础设施失败 → _send_toolchain_task @jiangwei-infra(防递归)""" - # 直接在 _toolchain DB 创建 task(不走 Gitea webhook) + """基础设施失败 → 直接在 _toolchain DB 创建 task @jiangwei-infra(防递归)""" try: - from src.api.toolchain_routes import _send_toolchain_task - _send_toolchain_task( - to_agent="jiangwei-infra", - title=f"[基础设施] Gitea API 不可用 - {task_id}", - description=( - f"Gitea API 不可用,原任务 {task_id} 无法通过正常路径处理。\n" - f"请检查 Gitea 服务状态和网络连通性。" - ), - event_type="infrastructure_failure", - action_type="infrastructure_failure", - steps=[ + from datetime import datetime + new_task_id = f"tc-{int(datetime.now().timestamp() * 1000)}" + must_hives = json.dumps({ + "event_type": "infrastructure_failure", + "action_type": "infrastructure_failure", + "steps": [ "检查 Gitea 服务状态(http://192.168.2.154:3000)", "检查网络连通性", "恢复后提交 action report", ], - context_data={"original_task_id": task_id, "verify_reason": verify.reason}, - source="toolchain_handler", + "context": {"original_task_id": task_id, "verify_reason": verify.reason}, + "from": "system", + "source": "toolchain_handler_on_failure", + }, ensure_ascii=False) + conn = get_connection(db_path) + conn.execute( + "INSERT INTO tasks (id, title, description, assignee, assigned_by, " + "must_haves, task_type, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + ( + new_task_id, + f"[基础设施] Gitea API 不可用 - {task_id}", + f"Gitea API 不可用,原任务 {task_id} 无法通过正常路径处理。\n" + f"请检查 Gitea 服务状态和网络连通性。", + "jiangwei-infra", + "system", + must_hives, + "toolchain", + "pending", + ) ) - logger.info("Toolchain %s: infrastructure failure → task created for jiangwei-infra", - task_id) + conn.commit() + conn.close() + logger.info( + "Toolchain %s: infrastructure failure → task %s created for jiangwei-infra", + task_id, new_task_id) except Exception as e: logger.error( "Toolchain %s: failed to create infrastructure_failure task: %s", diff --git a/tests/unit/test_toolchain_handler_v2.py b/tests/unit/test_toolchain_handler_v2.py index 3bcd311..620495b 100644 --- a/tests/unit/test_toolchain_handler_v2.py +++ b/tests/unit/test_toolchain_handler_v2.py @@ -398,12 +398,14 @@ class TestSendToolchainTask: class TestOnFailureRouting: def test_business_failure_creates_gitea_comment(self, handler, tmp_db): - """Business failure → Gitea PR comment""" + """Business failure → Gitea PR comment @task assignee (not must_hives field)""" + # S4: must_hives does NOT contain assignee — production data doesn't have it must_haves = json.dumps({ "action_type": "review_result", "context": {"repo": "sanguo/test", "pr_number": 42}, - "assignee": "zhangfei-dev", + "from": "system", }) + # assignee is set on the tasks table row (as production code writes it) _insert_task(tmp_db, "t-fail", must_haves) with patch.object(handler, "_create_gitea_comment") as mock_comment: @@ -414,9 +416,12 @@ class TestOnFailureRouting: call_args = mock_comment.call_args assert call_args[0][0] == "sanguo/test" assert call_args[0][1] == 42 + # M2: comment body should @ the task's assignee from tasks table + comment_body = call_args[0][2] + assert "@zhangfei-dev" in comment_body def test_infrastructure_failure_creates_task(self, handler, tmp_db): - """Infrastructure failure → _send_toolchain_task for jiangwei-infra""" + """Infrastructure failure → direct DB task for jiangwei-infra (no reverse dep)""" must_haves = json.dumps({ "action_type": "review_result", "context": {"repo": "sanguo/test", "pr_number": 42}, @@ -427,15 +432,22 @@ class TestOnFailureRouting: mock_comment.return_value = False # Gitea API down with patch.object(handler, "_create_gitea_issue") as mock_issue: mock_issue.return_value = False # Gitea API still down - with patch("src.api.toolchain_routes._send_toolchain_task") as mock_send: - mock_send.return_value = "tc-infra" - verify = VerifyResult(False, "no_action", "no action_report") - handler.on_failure("t-infra", "zhangfei-dev", tmp_db, verify) - # Should eventually try to create infrastructure_failure task - mock_send.assert_called() - call_kwargs = mock_send.call_args - assert call_kwargs[1]["action_type"] == "infrastructure_failure" - assert call_kwargs[1]["to_agent"] == "jiangwei-infra" + verify = VerifyResult(False, "no_action", "no action_report") + handler.on_failure("t-infra", "zhangfei-dev", tmp_db, verify) + + # S3: should directly INSERT into DB, not call _send_toolchain_task + # Verify a new task was created in DB for jiangwei-infra + conn = get_connection(tmp_db) + rows = conn.execute( + "SELECT * FROM tasks WHERE assignee=?", + ("jiangwei-infra",) + ).fetchall() + conn.close() + assert len(rows) >= 1, "No infrastructure_failure task created" + infra_task = rows[0] + assert infra_task["task_type"] == "toolchain" + meta = json.loads(infra_task["must_haves"]) + assert meta["action_type"] == "infrastructure_failure" # ---------------------------------------------------------------------------