diff --git a/docs/design/21-unified-toolchain-design.md b/docs/design/21-unified-toolchain-design.md index 9efc934..43d4a2c 100644 --- a/docs/design/21-unified-toolchain-design.md +++ b/docs/design/21-unified-toolchain-design.md @@ -1,9 +1,10 @@ --- title: "Unified Toolchain Design — 统一工具链工作流设计" created: 2026-06-20 -version: v1.0 draft +version: v1.1 draft status: draft -changelog: v1.0 初版 +changelog: v1.1 补充 §11b Issue opened 无 assignee 处理 + 修正 §13.1 触发路径 +v1.0 初版 --- # Unified Toolchain Design @@ -569,6 +570,125 @@ issue_closed 走 auto-pass(和 review_merged 一样),纯通知不需要 ag --- +## §11b. Issue opened 无 assignee 处理 + +### 11b.1 问题 + +`_handle_issues` 的 `action == "opened"` 分支只处理"部署失败"关键词和 @mention。无 assignee 的普通 Issue(如庞统创建的 parent Issue)被静默忽略,无法触发 discussion 流程。 + +### 11b.2 设计 + +`_handle_issues` 的 `opened` 分支新增无 assignee 处理路径: + +``` +Gitea Issue 创建(无 assignee, 有 type/* label) + → webhook: issues/opened + → _handle_issues: + action == "opened" + → 非"部署失败" + → 无 assignee + 有 type/* label + → 创建 toolchain task(assignee=None, action_type=issue_discussion) + → ticker 扫到 pending task + → router.route: assignee=None → 不走快速路径 4 → delegate 庞统 + → 庞统收到 discussion prompt(spawner._build_discussion_prompt) + → 庞统在 Issue 上 comment 发起讨论,引导其他 agent 参与 +``` + +### 11b.3 `_send_toolchain_task` 改动 + +`to_agent` 参数允许 `None`: + +```python +def _send_toolchain_task( + to_agent: str | None, # None = 无指派,待路由 + ... +) -> str: + # None 不校验 AGENT_IDS(非 None 时仍然校验) + if to_agent is not None and to_agent not in AGENT_IDS: + logger.warning("Unknown agent: %s, skipping toolchain task", to_agent) + return "" + + task = Task( + ... + assignee=to_agent, # None → 待路由 + ... + ) +``` + +### 11b.4 `_handle_issues` opened 分支改动 + +```python +elif action == "opened": + if "部署失败" in issue_title: + # 部署失败处理(不变) + ... + elif not (issue.get("assignees") or issue.get("assignee")): + # §11b: 无 assignee 的普通 Issue + labels_list = [lbl.get("name", "") + for lbl in (issue.get("labels") or [])] + has_type_label = any(lbl.lower().startswith("type/") for lbl in labels_list) + if not has_type_label: + return # 无 type label 不处理(避免噪音) + + title = f"Issue 讨论: {issue_title} ({repo}#{issue_number})" + _send_toolchain_task( + to_agent=None, # 无指派 → router delegate 庞统 + title=title, + description=f"## Issue 需要讨论\n\n{issue.get('body', '(无描述)')}", + event_type="issue_discussion", + action_type="issue_discussion", + steps=[], # discussion 不需要结构化步骤 + context_data={ + "issue_number": issue_number, + "repo": repo, + "issue_title": issue_title, + "issue_body": issue.get("body", ""), + }, + ) + return + + # @mention 检查(不变) + ... +``` + +### 11b.5 ToolchainHandler verify 改动 + +`issue_discussion` 走 auto-pass(纯触发,不需要 agent 执行步骤): + +```python +if meta.get("action_type") == "issue_discussion": + return VerifyResult(True, "discussion_passthrough", + "issue_discussion auto-pass") +``` + +### 11b.6 为什么用 delegate 而非 broadcast? + +现有 ticker 有两种调度路径: +- **deterministic**:有 assignee → 直接 spawn 给该 agent +- **broadcast**:无 assignee → 所有空闲 agent 收到 claim prompt + +无 assignee 的 Issue 如果走 broadcast,每个 agent 收到的是 claim prompt(认领 task),不是 discussion prompt(讨论 Issue)。而 §13.2 设计的 discussion prompt 才是正确行为——agent 应该讨论 Issue 内容,自主决定是否参与,而不是竞争认领一个 task。 + +**delegate 庞统**是更合理的路径: +- 庞统收到 task → 读取 Issue body → 按副军师职责发起讨论 +- 庞统在 Issue 上 comment 引导其他 agent 参与 +- 其他 agent 看到 @mention 后自主创建 sub Issue + +如果未来需要多 agent 并行讨论,可以在 ticker 中新增 discussion broadcast 路径(spawn_type=discussion)。但 MVP 阶段 delegate 庞统已足够。 + +### 11b.7 涉及改动 + +| 文件 | 改动 | +|------|------| +| `src/api/toolchain_routes.py` `_send_toolchain_task` | `to_agent` 允许 None | +| `src/api/toolchain_routes.py` `_handle_issues` | opened 分支加无 assignee 路径 | +| `src/daemon/toolchain_handler.py` `verify_completion` | issue_discussion auto-pass | +| `src/daemon/toolchain_handler.py` `_ACTION_HINTS` | 新增 issue_discussion | +| `src/daemon/toolchain_handler.py` `EVENT_LABELS_ZH` | 新增 issue_discussion | +| `tests/` | 新增 issue_discussion 测试 | + +--- + ## §12. AI Native 能力完整性(v2 补充) > 本节确保 Gitea 替代黑板后,PRD-v3.0 的 AI native 能力不降级。 @@ -606,12 +726,17 @@ issue_closed 走 auto-pass(和 review_merged 一样),纯通知不需要 ag 庞统创建 parent Issue(无 assignee)后,触发 discussion: ``` -庞统创建 parent Issue → webhook: issues/assigned(或 ticker 发现 pending 无 assignee) - → daemon 检测:无 assignee = 广播讨论 - → ticker 广播 spawn 所有 agent(spawn_type=discussion) - → 每个 agent 收到 DISCUSSION_PROMPT_TEMPLATE +庞统创建 parent Issue(无 assignee) + → Gitea webhook: issues/opened(注意:不是 assigned,无 assignee 的 Issue 只触发 opened) + → _handle_issues opened 分支检测:无 assignee + 有 type/* label + → 创建 toolchain task(assignee=None, action_type=issue_discussion) + → ticker 扫到 pending task → router delegate 庞统 + → 庞统收到 discussion prompt → 在 Issue 上 comment 发起讨论 + → 其他 agent 看到讨论 → 自主创建 sub Issue 认领 ``` +**关键修正(v1.1)**:原设计写 "webhook: issues/assigned",实际 Gitea 对无 assignee 的 Issue 只发 `issues/opened` 事件。`_handle_issues` 的 `opened` 分支需要新增无 assignee 处理路径(见 §11b)。 + ### 13.2 Discussion Prompt 设计(v3 重构) **设计参考**:Edict(角色驱动主动发言)+ APM(自包含 Task Prompt)+ PAV 循环(输入/输出/验证)。