diff --git a/docs/design/17-action-mail-type.md b/docs/design/17-action-mail-type.md index 0679a41..83706c5 100644 --- a/docs/design/17-action-mail-type.md +++ b/docs/design/17-action-mail-type.md @@ -238,6 +238,13 @@ action verify 失败时的处理逻辑与 request 一致: 2. 通过 `mail_notify.notify_mail_failed` 通知发件人 3. reason = `"no_action_report"`(收件人未执行动作) +### API 端点兼容性确认 + +action_report 提交使用的 `POST /api/projects/_mail/tasks/{task_id}/comments` 端点: +- `_mail` 已在 `_VIRTUAL_PROJECTS` 中支持(`blackboard_routes.py` L23) +- `comment_type` 参数已支持(`add_comment` endpoint: `body.get("comment_type", "general")`) +- **无需 API 改动**,现有端点完全兼容 action_report 提交 + --- ## §4. 场景分类与 type 分配 @@ -538,6 +545,19 @@ comment_type TEXT NOT NULL DEFAULT 'general' CHECK ( 或者更简单的方式:**去掉 CHECK 约束**。现有 CHECK 约束主要是文档作用,去掉不影响功能。 +#### 5.1.6 `src/daemon/mail_notify.py` + +**改动:`_REASON_MAP` 新增 `no_action_report` 条目** + +action Mail verify 失败时,reason 为 `no_action_report`。`mail_notify.py` 的 `_REASON_MAP` 需新增此 reason 的人话翻译,以便通知邮件内容清晰。 + +```python +_REASON_MAP = { + # ... 现有条目 ... + "no_action_report": "收件人未执行操作(未提交 action report)", +} +``` + ### 5.2 模板文件改动(`templates/toolchain/`) **全部 8 个 action 场景模板必须重写**。这不是"在现有模板上打补丁"——现有模板是按 inform 语义写的纯通知体,信息量薄。§15.5 原始流程强约束模板(含编号步骤和 API 调用指令)从未真正落地,被简化成了纯文本一句话。 @@ -578,8 +598,9 @@ comment_type TEXT NOT NULL DEFAULT 'general' CHECK ( | `src/daemon/spawner.py` | ~8 行 | 修改 | | `src/api/toolchain_routes.py` | ~120 行 | 修改 | | `src/blackboard/db.py` | ~5 行 | 修改 | +| `src/daemon/mail_notify.py` | ~3 行 | 修改 | | `templates/toolchain/*.md` | ~200 行 | 重写(8 个文件) | -| **总计** | **~420 行** | | +| **总计** | **~425 行** | | --- @@ -675,6 +696,8 @@ def _render_action(context: PromptContext) -> str: ) # [L3] 完成报告层 + # ⚠️ 实现注意:curl JSON 中的花括号需用 {{ }} 转义(f-string), + # 或单独构造 JSON 部分拼接,避免 SyntaxError report_block = ( "--- 完成后必须提交执行报告 ---\n" "执行完上述所有步骤后,必须提交 action report:\n\n" @@ -1000,10 +1023,11 @@ PR: http://192.168.2.154:3000/{repo}/pulls/{pr_number} ```python steps = [ - f"确认 CI 已通过: curl -s http://192.168.2.154:3000/api/v1/repos/{repo}/commits/{sha}/status " - f" -H 'Authorization: token {PAT}' → 检查 .state == 'success'", - f"如果 CI 未通过,先修复 CI 问题再合并", - f"合并 PR: curl -s -X POST http://192.168.2.154:3000/api/v1/repos/{repo}/pulls/{pr_number}/merge " + f"确认 CI 状态: curl -s http://192.168.2.154:3000/api/v1/repos/{repo}/commits/{sha}/status " + f" -H 'Authorization: token {PAT}' → 检查 .state", + f"如果 CI 还在 pending(state='pending'),等待 2-5 分钟后重新检查", + f"如果 CI 失败(state='failure'),先修复 CI 问题再合并", + f"CI 通过后(state='success'),合并 PR: curl -s -X POST http://192.168.2.154:3000/api/v1/repos/{repo}/pulls/{pr_number}/merge " f" -H 'Authorization: token {PAT}' -H 'Content-Type: application/json' " f" -d '{{\"Do\": \"merge\", \"merge_title_field\": \"Merge PR #{pr_number}\"}}'", f"确认合并成功: 检查返回的 PR 状态 .merged == true", @@ -1290,6 +1314,8 @@ steps = [ | `issue_comment` | `created` (含 @mention) | @mention | 被@者 | action | 场景 8 | | `deployment_status` | `failure` | 部署失败 | 庞统+姜维 | action | 场景 7 | +> **注**:PR #60 后 Gitea 也可通过 `X-Gitea-Event: pull_request_sync` header 直接路由,不走 `pull_request` action 分发。两种路由路径(`pull_request + synchronize` 和 `pull_request_sync`)都指向同一 Action 场景(场景 5)。 + **未覆盖的 Gitea Event**(评估后排除): | Event | 说明 | 排除理由 | @@ -1540,6 +1566,20 @@ def check_completion(self, task_id: str, db_path: Path) -> bool: action verify 失败时,复用 request 的 `on_failure` 逻辑(标 failed + 通知发件人)。reason 传 `"no_action_report"`,`mail_notify` 的 `_REASON_MAP` 新增此 reason 的人话翻译。 +### 9.3 action 与 ticker 超时的交互 + +action Mail 投递后 Agent 进入 working 状态。`_check_timeouts` 检查所有 working 任务(不区分 +performative),因此 action 类型同样受 `default_task_timeout_minutes`(默认 30 分钟)保护。 + +| 场景 | 处理 | +|------|------| +| Agent 正常执行完 → verify 通过 | 正常 done | +| Agent crash → 无 action_report | verify 标 failed | +| Agent 执行超时(>30分钟) | _check_timeouts 标 failed(working 超时兜底) | +| Agent 30 分钟内 crash 3 次 | crash_limit 标 failed | + +这是正确行为——action 步骤如果需要超长时间(如审查大 PR),应在 deadline 中调整。 + --- ## §10. 设计决策记录