From 1ec950a7cefed56afe16484e4bd5638619789320 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Wed, 24 Jun 2026 08:46:46 +0800 Subject: [PATCH 1/5] =?UTF-8?q?[moz]=20docs(=C2=A722):=20v1.3=20=C2=A722.6?= =?UTF-8?q?=20TC=20Discuss=20=E9=87=8D=E6=9E=84=20+=20=C2=A722.8=20TC=20Ro?= =?UTF-8?q?und=20Review=20Gitea=20=E9=80=82=E9=85=8D=E8=AE=BE=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/design/22-end-to-end-flow.md | 227 ++++++++++++++++++++++-------- 1 file changed, 172 insertions(+), 55 deletions(-) diff --git a/docs/design/22-end-to-end-flow.md b/docs/design/22-end-to-end-flow.md index ca33f0d..372065b 100644 --- a/docs/design/22-end-to-end-flow.md +++ b/docs/design/22-end-to-end-flow.md @@ -1,9 +1,10 @@ --- title: "End-to-End Flow — 端到端任务流程设计" created: 2026-06-22 -version: v1.2 +version: v1.3 status: draft -changelog: v1.2 §22.3/§22.4 更新 Phase 1 为 ✅ 已实现(PR #124) +changelog: v1.3 §22.6 重构为「所有 TC 流程第一步走 Discuss」+ §22.8 TC Round Review Gitea 适配设计 + v1.2 §22.3/§22.4 更新 Phase 1 为 ✅ 已实现(PR #124) v1.1 补充轻量路径设计(§22.6)、数据流澄清(§22.7)、修正设计原则3、统一Phase编号、Phase 1标注 v1.0 初版 --- @@ -264,35 +265,65 @@ issue_assigned: ## §22.6 轻量路径设计(Direct Assignment) -> 不是所有任务都需要 Discussion 广播。需求明确、单一执行者的任务应跳过 Phase 0/1 直接进入 Phase 2。 +> **核心原则:所有 TC 流程第一步都走 Discuss。** +> Discussion 不是可选项,而是 TC 流程的必经阶段——确保方案对齐、风险暴露后再进入 exec。 +> 区别只在于「谁参与讨论」。 ### 路径决策矩阵 -| 条件 | 路径 | 流程 | -|------|------|------| -| Issue 无 assignee + type/* label | **Discussion 路径**(默认) | Phase 0 → 1 → 2 → … | -| Issue 有 assignee | **Direct 路径** | → Phase 2(跳过 Phase 0/1) | -| Issue 有 `flow/direct` label | **强制 Direct** | → Phase 2(即使无 assignee,daemon 自动 assign) | -| Issue 有 `flow/discuss` label | **强制 Discussion** | Phase 0 → 1 → 2 → …(即使有 assignee) | +| 条件 | 讨论范围 | 流程 | +|------|---------|------| +| Issue 无 assignee + type/* label | **广播所有空闲 agent** | Phase 0 → 1(广播讨论)→ 2 → … | +| Issue 有 assignee | **assignee + reviewer(司马懿)定向讨论** | Phase 0 → 1(定向讨论)→ 2 → … | +| Issue 有 `flow/direct` label | **跳过讨论** | → Phase 2(唯一跳过 Discuss 的路径) | + +**设计原则**: +1. 没有 `flow/direct` label → 必须走 Discuss,没有例外 +2. 有 assignee → assignee 自己写方案 + 司马懿 review 通过后才能进 exec +3. 没有 assignee → 广播所有 agent 讨论,有 agent 认领后创建 sub Issue +4. `flow/direct` 是唯一的逃生舱(明确不需要讨论的小改动) ### 判断逻辑 ``` parent Issue 创建 - ├─ 有 flow/discuss label?→ Discussion 路径(Phase 0 → 1) - ├─ 有 flow/direct label?→ Direct 路径(daemon 自动 assign → Phase 2) - ├─ 有 assignee?→ Direct 路径(→ Phase 2) - └─ 无 assignee(默认)→ Discussion 路径(Phase 0 → 1) + ├─ 有 flow/direct label?→ Direct 路径(→ Phase 2,跳过讨论) + ├─ 有 assignee?→ 定向 Discussion(assignee + reviewer 讨论) + └─ 无 assignee(默认)→ 广播 Discussion(所有空闲 agent) ``` -优先级:`flow/*` label > assignee > 默认行为。 +### 定向讨论流程(有 assignee) + +``` +Issue assigned webhook + → daemon 创建 issue_discussion task(context_data 带 assignee) + → ticker dispatch:检测到 assignee 不为空 + → 只 spawn assignee + simayi-challenger(不广播其他人) + → discussion prompt 引导 assignee: + 1. 在 Gitea Issue comment 写实现方案(技术选型、实现路径、影响范围) + 2. 等待司马懿 review 方案 + 3. review 通过 → 创建 sub Issue assign 自己 → 进入 exec + 4. review 驳回 → 修改方案重新提交 +``` + +### 广播讨论流程(无 assignee) + +``` +Issue opened webhook + → daemon 创建 issue_discussion task(assignee=None) + → ticker broadcast:广播所有空闲 agent + → discussion prompt 引导每个 agent: + 1. 在 Gitea Issue comment 回应(定位/建议/认领/风险) + 2. 需要参与的 agent 创建 sub Issue assign 自己 +``` ### 适用场景 | 路径 | 适用场景 | 示例 | |------|---------|------| -| Discussion | 需求不明确、需要多角色讨论、跨模块协作 | 新功能设计、架构变更、涉及 3+ agent 的任务 | -| Direct | 需求明确、单一执行者、小范围改动 | Bug 修复、文档更新、配置调整、单人任务 | +| 广播讨论 | 需求不明确、跨模块协作、无人认领 | 新功能设计、架构变更 | +| 定向讨论 | 需求明确但需要方案确认 | 有明确 assignee 的 Issue | +| Direct | 改 typo、改配置值等极小改动 | `flow/direct` label | ### Discussion 路径中的降级 @@ -304,26 +335,18 @@ Discussion 进行中,如果所有 agent 都认为任务足够简单只涉及 庞统判断后创建 sub Issue 直接 assign → 该 agent 跳过讨论直接进入 Phase 2。 -### Direct 路径跳过的 Phase +### 实现方案 -| Phase | 是否跳过 | 原因 | -|-------|---------|------| -| Phase 0(parent Issue 讨论 task) | ✅ 跳过 | 不创建 discussion task | -| Phase 1(Discussion 广播) | ✅ 跳过 | 不广播,不打扰其他 agent | -| Phase 2(assigned → executor) | ❌ 直接进入 | 起点 | +**toolchain_routes.py**: +- `opened` 分支(无 assignee)→ discussion task(assignee=None)— 现有逻辑 ✅ +- `assigned` 分支改为:创建 discussion task(context_data 带 assignee)— **需要改动** +- `flow/direct` 分支 → executor task — 现有逻辑 ✅ -### 对现有代码的影响 +**ticker.py `_broadcast_claim`**: +- discussion task + `context.assignee` 有值 → 只 spawn assignee + simayi-challenger +- discussion task + `context.assignee` 为空 → 广播所有空闲 agent(现有逻辑 ✅) -当前 `_handle_issues` 已有两个分支: - -- `opened`(无 assignee + type/* label)→ discussion task ← Discussion 路径 ✅ -- `assigned`(有 assignee)→ executor task ← Direct 路径 ✅ - -**轻量路径已全部实现**。包括: - -1. ✅ `flow/direct` 和 `flow/discuss` label 已创建(Gitea label id=100, 101) -2. ✅ `toolchain_routes.py` `_handle_issues` opened 分支识别 `flow/direct` → executor task -3. ✅ Discussion prompt 中包含降级机制引导(§22.6) +**不影响**:mail / task 流程(它们不走 toolchain_routes.py) --- @@ -360,29 +383,11 @@ ticker: _check_round_complete → 全部终态 → spawn 庞统 review ``` -### 前置条件 +### 前置条件(已全部实现 ✅) -1. **task_state 表创建**:§20 设计了 DDL 但未实现。需要先建表: - - ```sql - CREATE TABLE task_state ( - issue_number INTEGER PRIMARY KEY, - repo TEXT, - parent_issue INTEGER, - status TEXT DEFAULT 'pending', - action_type TEXT, - retry_count INTEGER DEFAULT 0, - dispatch_count INTEGER DEFAULT 0, - round_count INTEGER DEFAULT 0, - created_at TEXT, - updated_at TEXT - ); - CREATE INDEX idx_task_state_parent ON task_state(parent_issue); - ``` - -2. **parent_issue 解析**:daemon 在 `_handle_issues` 中解析标题 `[parent #{N}]` 模式,写入 `task_state.parent_issue`(§21 §14b L920 已有此设计)。 - -3. **_check_round_complete 改造**:从扫 `tasks.parent_task`(黑板)改为扫 `task_state.parent_issue`(toolchain DB)(§21 §16.2 L1148 已有此设计)。 +1. **task_state 表创建**:已在 PR #125 中实现(`_init_task_state_table`,CREATE IF NOT EXISTS)。 +2. **parent_issue 解析**:已实现(`_ensure_task_state`,`_handle_issues` assigned 分支解析 `[parent #N]`)。 +3. **_check_round_complete 双源扫描**:已实现(`task_state.parent_issue` + `tasks.parent_task`)。 ### 混合期处理 @@ -405,3 +410,115 @@ task_state 表创建前,`_check_round_complete` 仍扫 `tasks.parent_task`。 | 5 | toolchain DB: task(review_merged) | Gitea webhook (PR closed/merged) | — | — | | 6 | task_state: round_count++ | task_state: parent_issue + sub status | Gitea parent Issue body + sub Issue comments | Gitea Issue comment(三问结论)+ 关闭/创建 Issue | | 7 | toolchain DB: task(issue_closed) | Gitea webhook (issues/closed) | — | — | + +--- + +## §22.8 TC Round Review Gitea 适配 + +> **设计原则:三个流程已通过 Handler 架构分离(§14)。TC Round Review 的改造通过 ToolchainHandler 分流,不影响黑板流程。** + +### 问题 + +`_check_round_complete` 中调用 `self._build_review_prompt`(黑板 API 版本),对 TC 项目也用黑板 API。但 TC 项目的协作面在 Gitea,数据源不一致。 + +### 分流方案 + +通过 Handler 架构分流,和 `_dispatch_reviews`(L1379 已有 `handler = get_by_project(); if handler: return []`)一样的模式: + +``` +_check_round_complete: + handler = TaskTypeRegistry.get_by_project(project_id) + if handler and handler.virtual_project == "_toolchain": + # TC 路径:用 Gitea API 版 review prompt + review_prompt = handler.build_round_review_prompt(parent_id, ...) + else: + # 黑板路径:原有逻辑不变 + review_prompt = self._build_review_prompt(parent_task, summary, ...) +``` + +### BaseTaskHandler 接口扩展 + +在 `BaseTaskHandler`(`task_type_registry.py`)中新增默认方法: + +```python +def build_round_review_prompt(self, parent_id: str, summary: dict, + outputs: list, comments: list, + round_num: int, **kwargs) -> str: + """构建 Round Review prompt。默认 raise NotImplementedError。""" + raise NotImplementedError( + f"{self.task_type} handler does not support round review") +``` + +普通 task 和 mail 不调用此方法(走 ticker 原有 `_build_review_prompt`),只有 ToolchainHandler override。 + +### ToolchainHandler.build_round_review_prompt + +TC 版本的 Round Review prompt。核心区别: + +| 维度 | 黑板版(原) | Gitea 版(新) | +|------|-----------|-------------| +| Goal 来源 | `parent_task.description`(黑板 DB) | parent Issue body(庞统自己读 Gitea API) | +| 成果物 | `bb.get_aggregate_outputs()`(黑板 DB) | PR / commit(庞统自己读 Gitea API) | +| 讨论历史 | `bb.get_round_comments()`(黑板 DB) | Issue comments(庞统自己读 Gitea API) | +| 创建新 sub | `POST localhost:8083/api/.../tasks`(黑板 API) | `POST Gitea /repos/.../issues`(Gitea API) | +| 关闭 parent | 更新黑板 status | `PATCH Gitea /repos/.../issues/{N}` | + +**prompt 设计**:只给 Issue 编号和 repo,让庞统 spawn 后自己调 Gitea API 读详情。daemon 不做 Gitea API 调用。 + +``` +## 庞统 TC Round Review(第 {round_num} 轮) + +### Parent Issue +- Repo: {repo} +- Issue: #{parent_issue_number} + +### 本轮 Sub Issue 状态 +- 完成: {done} +- 失败: {failed} +- 总计: {total} + +### 你必须做什么 + +1. 读 parent Issue: GET /repos/{repo}/issues/{parent_issue_number} +2. 读所有 sub Issue 的 comments 和 PR +3. 三问评估: + - Goal 还清晰吗? + - 成果物覆盖 goal 了吗?(逐条检查 + docs/design 同步) + - 下一轮需要做什么? +4. 决策: + - GOAL_ACHIEVED → 关闭 parent Issue + - 需要新轮 → 创建新 sub Issues + +### Gitea API +- 读 Issue: GET /repos/{repo}/issues/{N} +- 读 comments: GET /repos/{repo}/issues/{N}/comments +- 创建 sub Issue: POST /repos/{repo}/issues +- 关闭 Issue: PATCH /repos/{repo}/issues/{N} (state=closed) +``` + +### 不影响黑板流程的保证 + +1. **Handler 分流**:`_check_round_complete` 通过 `TaskTypeRegistry.get_by_project(project_id)` 判断。普通项目(无 handler)走 `_build_review_prompt`(黑板版本不变) +2. **数据隔离**:TC review 读 `task_state` 表 + Gitea API;黑板 review 读 `tasks` 表 + `outputs` / `comments` 表 +3. **prompt 隔离**:TC prompt 只含 Gitea API 指引;黑板 prompt 只含黑板 API 指引 +4. **`_dispatch_reviews` 已有先例**:L1379 `if handler: return []` 已经用 Handler 分流跳过 handler 项目的 review 流程 + +### 实现方案 + +| 文件 | 改动 | +|------|------| +| `task_type_registry.py` | `BaseTaskHandler` 加 `build_round_review_prompt` 默认方法(raise NotImplementedError) | +| `toolchain_handler.py` | `ToolchainHandler` override `build_round_review_prompt` → 返回 Gitea API 版 prompt | +| `ticker.py` | `_check_round_complete` 中 handler 分流:有 handler 用 handler 方法,否则用 `_build_review_prompt` | + +### TaskState round_count 同步 + +TC 路径的 `round_count` 存在 `task_state` 表中。`_check_round_complete` 需要在 spawn review 成功后递增: + +```python +if handler and handler.virtual_project == "_toolchain": + # TC 路径:递增 task_state.round_count + conn.execute("UPDATE task_state SET round_count = round_count + 1 WHERE issue_number = ?", ...) +else: + # 黑板路径:原有 bb.increment_round_count(parent_id) +``` -- 2.45.4 From a376fdadbc4314bb8b2071b14ed076df2ad8a105 Mon Sep 17 00:00:00 2001 From: claude_dev Date: Thu, 25 Jun 2026 07:41:17 +0800 Subject: [PATCH 2/5] =?UTF-8?q?[moz]=20docs(=C2=A722):=20v1.4=20=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E8=A1=A5=E8=B6=B3=20=E2=80=94=206=20=E4=B8=AA?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D=EF=BC=88=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E8=AE=BE=E6=96=BD=E6=8E=92=E9=99=A4/=E5=8D=95spawn=E8=87=AA?= =?UTF-8?q?=E4=B8=BB=E6=A8=A1=E5=BC=8F/=E8=BF=81=E7=A7=BB=E7=AD=96?= =?UTF-8?q?=E7=95=A5/TC=E6=95=B0=E6=8D=AE=E6=BA=90=E5=88=86=E6=B5=81?= =?UTF-8?q?=E9=80=9A=E9=80=8F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/design/22-end-to-end-flow.md | 186 ++++++++++++++++++++++++------ 1 file changed, 153 insertions(+), 33 deletions(-) diff --git a/docs/design/22-end-to-end-flow.md b/docs/design/22-end-to-end-flow.md index 372065b..42dc2fc 100644 --- a/docs/design/22-end-to-end-flow.md +++ b/docs/design/22-end-to-end-flow.md @@ -1,9 +1,10 @@ --- title: "End-to-End Flow — 端到端任务流程设计" created: 2026-06-22 -version: v1.3 +version: v1.4 status: draft -changelog: v1.3 §22.6 重构为「所有 TC 流程第一步走 Discuss」+ §22.8 TC Round Review Gitea 适配设计 +changelog: v1.4 §22.6/§22.8 设计补足:基础设施排除、定向讨论简化为单spawn自主模式、迁移策略、TC数据源/round_count分流通透 + v1.3 §22.6 重构为「所有 TC 流程第一步走 Discuss」+ §22.8 TC Round Review Gitea 适配设计 v1.2 §22.3/§22.4 更新 Phase 1 为 ✅ 已实现(PR #124) v1.1 补充轻量路径设计(§22.6)、数据流澄清(§22.7)、修正设计原则3、统一Phase编号、Phase 1标注 v1.0 初版 @@ -265,7 +266,7 @@ issue_assigned: ## §22.6 轻量路径设计(Direct Assignment) -> **核心原则:所有 TC 流程第一步都走 Discuss。** +> **核心原则:所有 TC 流程第一步都走 Discuss(基础设施和 flow/direct 除外)。** > Discussion 不是可选项,而是 TC 流程的必经阶段——确保方案对齐、风险暴露后再进入 exec。 > 区别只在于「谁参与讨论」。 @@ -274,14 +275,17 @@ issue_assigned: | 条件 | 讨论范围 | 流程 | |------|---------|------| | Issue 无 assignee + type/* label | **广播所有空闲 agent** | Phase 0 → 1(广播讨论)→ 2 → … | -| Issue 有 assignee | **assignee + reviewer(司马懿)定向讨论** | Phase 0 → 1(定向讨论)→ 2 → … | -| Issue 有 `flow/direct` label | **跳过讨论** | → Phase 2(唯一跳过 Discuss 的路径) | +| Issue 有 assignee(非 infrastructure) | **assignee 自主定向讨论** | assignee 写方案 → @司马懿 review → 创建 sub Issue 进 exec | +| Issue 有 `type/infrastructure` label | **跳过讨论** | → executor(运维排障,无需方案审查) | +| Issue 有 `flow/direct` label | **跳过讨论** | → Phase 2(极小改动逃生舱) | **设计原则**: -1. 没有 `flow/direct` label → 必须走 Discuss,没有例外 -2. 有 assignee → assignee 自己写方案 + 司马懿 review 通过后才能进 exec -3. 没有 assignee → 广播所有 agent 讨论,有 agent 认领后创建 sub Issue -4. `flow/direct` 是唯一的逃生舱(明确不需要讨论的小改动) +1. 基础设施 Issue(`type/infrastructure`)→ 直接到 executor(运维排障任务,不需要写方案+审查) +2. `flow/direct` label → 直接到 executor(明确不需要讨论的小改动) +3. 有 assignee(非 infrastructure)→ assignee 自主走「方案→review→exec」流程,daemon 不编排 +4. 没有 assignee → 广播所有 agent 讨论,有 agent 认领后创建 sub Issue + +> **v1.4 补充**:v1.3 原设计将 infrastructure 也纳入 Discuss,但基础设施排障任务(Gitea 挂了、磁盘满等)不需要写实现方案和司马懿审查,应排除。 ### 判断逻辑 @@ -292,20 +296,32 @@ parent Issue 创建 └─ 无 assignee(默认)→ 广播 Discussion(所有空闲 agent) ``` -### 定向讨论流程(有 assignee) +### 定向讨论流程(有 assignee,非 infrastructure) + +> **v1.4 修正**:v1.3 原设计「ticker 同时 spawn assignee + 司马懿」过于复杂,需要 daemon 协调两个 agent 生命周期。 +> v1.4 简化为**单 spawn 自主模式**——daemon 只创建 discussion task 给 assignee,assignee 在一个 session 内自主完成全部流程。 ``` Issue assigned webhook - → daemon 创建 issue_discussion task(context_data 带 assignee) - → ticker dispatch:检测到 assignee 不为空 - → 只 spawn assignee + simayi-challenger(不广播其他人) + → daemon 创建 issue_discussion task(assignee=该 agent,context_data 带 issue/repo) + → ticker dispatch:有 assignee → 确定性路由 spawn assignee 单人 → discussion prompt 引导 assignee: - 1. 在 Gitea Issue comment 写实现方案(技术选型、实现路径、影响范围) - 2. 等待司马懿 review 方案 - 3. review 通过 → 创建 sub Issue assign 自己 → 进入 exec - 4. review 驳回 → 修改方案重新提交 + 1. 读 Issue 全文,在 Gitea Issue comment 写实现方案(技术选型、实现路径、影响范围) + 2. @simayi-challenger 请求方案审查(利用现有 @mention 机制自动创建 review task) + 3. 等司马懿 review 结果(通过 @mention 回传) + 4. review 通过 → 创建 sub Issue(assign 自己)→ 进入 exec + 5. review 驳回 → 修改方案重新提交(重新 comment + @司马懿) ``` +**为什么不双 spawn**: +1. assignee 写方案时间不确定,双 spawn 后司马懿可能干等 +2. 两阶段串行更简单,assignee 完成后 @mention 自动触发司马懿 +3. 和广播讨论一致——daemon 只创建初始 task,后续靠 agent 自主 + @mention + +**与广播讨论的区别**: +- 广播讨论:assignee=None → ticker 归入 broadcast_tasks → 广播所有空闲 agent +- 定向讨论:assignee=该 agent → ticker 归入 deterministic_tasks → 确定性路由 spawn 单人 + ### 广播讨论流程(无 assignee) ``` @@ -335,16 +351,38 @@ Discussion 进行中,如果所有 agent 都认为任务足够简单只涉及 庞统判断后创建 sub Issue 直接 assign → 该 agent 跳过讨论直接进入 Phase 2。 +### 迁移策略 + +> **v1.4 补充**:v1.3 未说明现有 `issue_assigned` 路径如何处理。 + +**保留 `issue_assigned` 给 infrastructure / flow-direct,新增定向讨论路径**: + +| 条件 | 旧路径(v1.2) | 新路径(v1.4) | 变化 | +|------|----------------|----------------|------| +| 有 assignee + 非 infrastructure | issue_assigned → executor | issue_discussion → discussion(定向) | **改** | +| 有 assignee + `type/infrastructure` | issue_assigned → executor | issue_assigned → executor(不变) | 无 | +| 有 `flow/direct` label | issue_assigned → executor | issue_assigned → executor(不变) | 无 | +| 无 assignee + type/* label | issue_discussion → broadcast | issue_discussion → broadcast(不变) | 无 | + +`_ACTION_HINTS` / YAML steps / `ToolchainContextSection` 中的 `issue_assigned` 相关逻辑**保留**(infrastructure/flow-direct 还用),新增 `issue_review_discussion` action_type。 + ### 实现方案 **toolchain_routes.py**: - `opened` 分支(无 assignee)→ discussion task(assignee=None)— 现有逻辑 ✅ -- `assigned` 分支改为:创建 discussion task(context_data 带 assignee)— **需要改动** -- `flow/direct` 分支 → executor task — 现有逻辑 ✅ +- `assigned` 分支改造: + - `is_infrastructure=True` → executor task(现有逻辑保留 ✅) + - `has_flow_direct=True` → executor task(现有逻辑保留 ✅) + - **其他** → discussion task(assignee=该 agent,context_data 带 issue/repo)— **需要改动** -**ticker.py `_broadcast_claim`**: -- discussion task + `context.assignee` 有值 → 只 spawn assignee + simayi-challenger -- discussion task + `context.assignee` 为空 → 广播所有空闲 agent(现有逻辑 ✅) +**ticker.py**: +- 定向讨论 task 有 assignee → `_dispatch_pending` 确定性路由直接 spawn assignee 单人 +- 广播讨论 task 无 assignee → `_broadcast_claim` 广播所有空闲 agent(现有逻辑 ✅) +- `_broadcast_claim` 中的 action_type 判断逻辑保留(discussion task 走 `_build_discussion_prompt`) + +**讨论 prompt 差异化**: +- 定向讨论 prompt 额外指引:你是 assignee,需要写方案并主动 @司马懿 review +- 广播讨论 prompt 保持现有模板(4 维度回应 + 自主认领) **不影响**:mail / task 流程(它们不走 toolchain_routes.py) @@ -417,23 +455,64 @@ task_state 表创建前,`_check_round_complete` 仍扫 `tasks.parent_task`。 > **设计原则:三个流程已通过 Handler 架构分离(§14)。TC Round Review 的改造通过 ToolchainHandler 分流,不影响黑板流程。** -### 问题 +### 问题(v1.4 修正:不只是 prompt 不同,数据查询链路也不同) -`_check_round_complete` 中调用 `self._build_review_prompt`(黑板 API 版本),对 TC 项目也用黑板 API。但 TC 项目的协作面在 Gitea,数据源不一致。 +`_check_round_complete` 中调用 `bb.get_subtasks_summary(parent_id)` / `bb.get_aggregate_outputs()` / `bb.get_round_comments()` 全部查黑板 DB。但 TC 的子任务信息在 `task_state` 表 + Gitea Issue/PR,**根本不在黑板 tasks/outputs/comments 表**。 -### 分流方案 +因此 handler 分流不只是 prompt 层面,需要贯穿**数据查询 → prompt 构建 → round_count 递增**三个环节。 -通过 Handler 架构分流,和 `_dispatch_reviews`(L1379 已有 `handler = get_by_project(); if handler: return []`)一样的模式: +### TC 数据源 vs 黑板数据源对照 + +| 环节 | 黑板路径 | TC 路径 | +|------|---------|---------| +| 子任务状态 | `bb.get_subtasks_summary()` → tasks 表 | 新增 `get_tc_subtask_summary()` → task_state 表 | +| 成果物 | `bb.get_aggregate_outputs()` → outputs 表 | 庞统自己读 Gitea API(PR/commit) | +| 讨论历史 | `bb.get_round_comments()` → comments 表 | 庞统自己读 Gitea Issue comments | +| round_count | `bb.increment_round_count()` → tasks.round_count | `UPDATE task_state SET round_count+1` | +| parent status | tasks.status | task_state.status(需补充更新逻辑) | + +### task_state.status 更新机制 + +> **v1.4 补充**:当前 `_ensure_task_state` 只做 `INSERT OR IGNORE`,status 初始为 `pending`,后续没有任何代码更新。需补充。 + +**更新来源**:Gitea Issue closed webhook → 更新 `task_state.status = 'done'` + +``` +_handle_issues: action == "closed" + → UPDATE task_state SET status='done', updated_at=datetime('now') + WHERE issue_number = ? +``` + +后续如需更精细状态(failed/retry),通过 ToolchainHandler.post_complete 补充更新。 + +### 分流方案(全链路) ``` _check_round_complete: handler = TaskTypeRegistry.get_by_project(project_id) - if handler and handler.virtual_project == "_toolchain": - # TC 路径:用 Gitea API 版 review prompt - review_prompt = handler.build_round_review_prompt(parent_id, ...) + is_tc = handler and handler.virtual_project == "_toolchain" + + # 1. 数据查询分流 + if is_tc and parent_id in task_state_parents: + summary = get_tc_subtask_summary(tc_db, int(parent_id)) + # 不查 outputs/comments — 庞统 spawn 后自己读 Gitea API else: - # 黑板路径:原有逻辑不变 - review_prompt = self._build_review_prompt(parent_task, summary, ...) + summary = bb.get_subtasks_summary(parent_id) + outputs = bb.get_aggregate_outputs(parent_id) + comments = bb.get_round_comments(parent_id) + + # 2. prompt 构建分流 + if is_tc: + review_prompt = handler.build_round_review_prompt(parent_id, summary, ...) + else: + review_prompt = self._build_review_prompt(parent_task, summary, outputs, comments, ...) + + # 3. round_count 递增分流 + if spawned: + if is_tc: + UPDATE task_state SET round_count = round_count + 1 WHERE issue_number = ? + else: + bb.increment_round_count(parent_id) ``` ### BaseTaskHandler 接口扩展 @@ -508,8 +587,49 @@ TC 版本的 Round Review prompt。核心区别: | 文件 | 改动 | |------|------| | `task_type_registry.py` | `BaseTaskHandler` 加 `build_round_review_prompt` 默认方法(raise NotImplementedError) | -| `toolchain_handler.py` | `ToolchainHandler` override `build_round_review_prompt` → 返回 Gitea API 版 prompt | -| `ticker.py` | `_check_round_complete` 中 handler 分流:有 handler 用 handler 方法,否则用 `_build_review_prompt` | +| `toolchain_handler.py` | `ToolchainHandler` override `build_round_review_prompt` → 返回 Gitea API 版 prompt;新增 `get_tc_subtask_summary()` 静态方法 | +| `ticker.py` | `_check_round_complete` 全链路分流:数据查询 + prompt 构建 + round_count 递增 | +| `toolchain_routes.py` | `_handle_issues` closed 分支:更新 `task_state.status = 'done'` | + +### get_tc_subtask_summary 方法设计 + +```python +def get_tc_subtask_summary(tc_db: Path, parent_issue: int) -> Optional[dict]: + """TC 路径:从 task_state 表查子任务状态摘要""" + conn = get_connection(tc_db) + try: + parent_row = conn.execute( + "SELECT round_count FROM task_state WHERE issue_number = ?", + (parent_issue,) + ).fetchone() + if not parent_row: + return None + + rows = conn.execute( + "SELECT status, COUNT(*) as cnt FROM task_state " + "WHERE parent_issue = ? GROUP BY status", + (parent_issue,) + ).fetchall() + if not rows: + return None + + summary = { + "parent_id": str(parent_issue), + "parent_status": "done", # TC 路径 parent 是 Gitea Issue + "round_count": parent_row["round_count"], + "total": 0, "done": 0, "failed": 0, "cancelled": 0, "other": 0, + } + for row in rows: + summary["total"] += row["cnt"] + if row["status"] in ("done", "failed", "cancelled"): + summary[row["status"]] += row["cnt"] + else: + summary["other"] += row["cnt"] + summary["all_terminal"] = summary["other"] == 0 and summary["total"] > 0 + return summary + finally: + conn.close() +``` ### TaskState round_count 同步 -- 2.45.4 From c19286eb5b128b08f040567016ab3dffb9d4dd8a Mon Sep 17 00:00:00 2001 From: claude_dev Date: Thu, 25 Jun 2026 07:45:26 +0800 Subject: [PATCH 3/5] =?UTF-8?q?[moz]=20docs(=C2=A722):=20v1.5=20=E9=87=87?= =?UTF-8?q?=E7=BA=B3=E5=8F=B8=E9=A9=AC=E6=87=BF=20S1-S4+P1-P3=20=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5=E5=BB=BA=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/design/22-end-to-end-flow.md | 95 ++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 14 deletions(-) diff --git a/docs/design/22-end-to-end-flow.md b/docs/design/22-end-to-end-flow.md index 42dc2fc..0b949a4 100644 --- a/docs/design/22-end-to-end-flow.md +++ b/docs/design/22-end-to-end-flow.md @@ -1,9 +1,10 @@ --- title: "End-to-End Flow — 端到端任务流程设计" created: 2026-06-22 -version: v1.4 +version: v1.5 status: draft -changelog: v1.4 §22.6/§22.8 设计补足:基础设施排除、定向讨论简化为单spawn自主模式、迁移策略、TC数据源/round_count分流通透 +changelog: v1.5 采纳司马懿 S1-S4+P1-P3:判断逻辑补 infrastructure 分支、flow/discuss deprecated、定向讨论 fallback、task_state.status 完整生命周期、parent_status 查表不硬编码 + v1.4 §22.6/§22.8 设计补足:基础设施排除、定向讨论简化为单spawn自主模式、迁移策略、TC数据源/round_count分流通透 v1.3 §22.6 重构为「所有 TC 流程第一步走 Discuss」+ §22.8 TC Round Review Gitea 适配设计 v1.2 §22.3/§22.4 更新 Phase 1 为 ✅ 已实现(PR #124) v1.1 补充轻量路径设计(§22.6)、数据流澄清(§22.7)、修正设计原则3、统一Phase编号、Phase 1标注 @@ -292,7 +293,8 @@ issue_assigned: ``` parent Issue 创建 ├─ 有 flow/direct label?→ Direct 路径(→ Phase 2,跳过讨论) - ├─ 有 assignee?→ 定向 Discussion(assignee + reviewer 讨论) + ├─ 有 type/infrastructure label?→ Direct 路径(→ executor,运维排障) + ├─ 有 assignee(非 infrastructure)?→ 定向 Discussion(assignee 自主模式) └─ 无 assignee(默认)→ 广播 Discussion(所有空闲 agent) ``` @@ -322,6 +324,21 @@ Issue assigned webhook - 广播讨论:assignee=None → ticker 归入 broadcast_tasks → 广播所有空闲 agent - 定向讨论:assignee=该 agent → ticker 归入 deterministic_tasks → 确定性路由 spawn 单人 +### 定向讨论 fallback(assignee 无响应) + +> **v1.4 补充(司马懿 S4)**:广播讨论有「3 轮无 taker → 升级庞统」机制,定向讨论也需要 fallback。 + +单 spawn assignee 后,如果 assignee NO_REPLY 或 session 超时: + +``` +ticker 定期 check(复用现有 broadcast _broadcast_tracker 机制) + → discussion task 仍 pending/working 且超过 3 轮 check 未终态 + → 升级庞统(escalated 状态) + → 庞统判断:重新分配 / 转为广播讨论 / 直接关闭 +``` + +复用现有 `_broadcast_tracker` 的 `round_number >= 3 → escalated` 逻辑,不引入新机制。 + ### 广播讨论流程(无 assignee) ``` @@ -337,7 +354,7 @@ Issue opened webhook | 路径 | 适用场景 | 示例 | |------|---------|------| -| 广播讨论 | 需求不明确、跨模块协作、无人认领 | 新功能设计、架构变更 | +| 广播讨论 | 需求不明确、跨模块协作、涉及 3+ agent、无人认领 | 新功能设计、架构变更 | | 定向讨论 | 需求明确但需要方案确认 | 有明确 assignee 的 Issue | | Direct | 改 typo、改配置值等极小改动 | `flow/direct` label | @@ -366,6 +383,8 @@ Discussion 进行中,如果所有 agent 都认为任务足够简单只涉及 `_ACTION_HINTS` / YAML steps / `ToolchainContextSection` 中的 `issue_assigned` 相关逻辑**保留**(infrastructure/flow-direct 还用),新增 `issue_review_discussion` action_type。 +> **`flow/discuss` label deprecated**:v1.2 曾设计 `flow/discuss` label(强制 Discussion 即使有 assignee),v1.4 废弃。所有有 assignee 的 Issue(非 infrastructure)默认走定向讨论,无需显式 label。Gitea 上 `flow/discuss` label(id=101)保留但不影响路由。 + ### 实现方案 **toolchain_routes.py**: @@ -473,17 +492,65 @@ task_state 表创建前,`_check_round_complete` 仍扫 `tasks.parent_task`。 ### task_state.status 更新机制 -> **v1.4 补充**:当前 `_ensure_task_state` 只做 `INSERT OR IGNORE`,status 初始为 `pending`,后续没有任何代码更新。需补充。 +> **v1.4 补充(司马懿 S3)**:当前 `_ensure_task_state` 只做 `INSERT OR IGNORE`,status 初始为 `pending`,后续没有任何代码更新。需补充。 -**更新来源**:Gitea Issue closed webhook → 更新 `task_state.status = 'done'` +**状态生命周期**: -``` -_handle_issues: action == "closed" - → UPDATE task_state SET status='done', updated_at=datetime('now') - WHERE issue_number = ? +| 状态 | 触发来源 | 更新位置 | +|------|---------|----------| +| `pending` | `_ensure_task_state` 初始插入 | `toolchain_routes.py` assigned 分支 | +| `done` | Gitea Issue closed webhook | `toolchain_routes.py` closed 分支 | +| `failed` | executor task 验证失败且未重试 | `toolchain_handler.py` `post_complete` → `_update_task_state_status()` | + +**关键路径说明**: + +1. **sub Issue 创建** → `_ensure_task_state` 写入 `status='pending'` +2. **sub Issue 对应的 executor task 完成** → ToolchainHandler 通过 @mention 指引 agent 创建 PR(含 `Closes #N`)→ PR 合并时 Gitea 自动关闭 Issue → webhook `issues/closed` → 更新 `status='done'` +3. **executor task 失败** → `ToolchainHandler.post_complete()` 中检测 verify 失败且 retry_count 达上限 → 调用 `_update_task_state_status(issue_number, 'failed')` + +**`post_complete` 何时触发**:spawner 完成 session 后(agent exit),ticker 在下个 tick 检测到 session 终态 → 调用 `handler.post_complete(task_id, agent_id, outcome, db_path)`。此时 handler 可以: +- 检查 verify 结果 +- 如果 failed 且不可 retry → 查 task_state 找到对应 issue_number → 更新 status='failed' + +**新增方法**: + +```python +# toolchain_handler.py +def _update_task_state_status(self, issue_number: int, status: str): + """更新 task_state.status(executor 失败时调用)""" + tc_db = _toolchain_db_path() + conn = get_connection(tc_db) + try: + conn.execute( + "UPDATE task_state SET status=?, updated_at=datetime('now') " + "WHERE issue_number=?", + (status, issue_number) + ) + conn.commit() + finally: + conn.close() ``` -后续如需更精细状态(failed/retry),通过 ToolchainHandler.post_complete 补充更新。 +**toolchain_routes.py closed 分支补充**: + +```python +elif action == "closed": + # 更新 task_state status='done' + tc_db = _toolchain_db_path() + conn = get_connection(tc_db) + try: + conn.execute( + "UPDATE task_state SET status='done', updated_at=datetime('now') " + "WHERE issue_number=?", + (issue_number,) + ) + conn.commit() + finally: + conn.close() + # ... 现有通知逻辑 +``` + +> **注意**:`get_tc_subtask_summary` 中的 `all_terminal` 只依赖 `done` 和 `failed` 两种终态。`pending`/`working` 计入 `other`,确保未完成的 sub 不会误触发 round review。 ### 分流方案(全链路) @@ -580,7 +647,7 @@ TC 版本的 Round Review prompt。核心区别: 1. **Handler 分流**:`_check_round_complete` 通过 `TaskTypeRegistry.get_by_project(project_id)` 判断。普通项目(无 handler)走 `_build_review_prompt`(黑板版本不变) 2. **数据隔离**:TC review 读 `task_state` 表 + Gitea API;黑板 review 读 `tasks` 表 + `outputs` / `comments` 表 3. **prompt 隔离**:TC prompt 只含 Gitea API 指引;黑板 prompt 只含黑板 API 指引 -4. **`_dispatch_reviews` 已有先例**:L1379 `if handler: return []` 已经用 Handler 分流跳过 handler 项目的 review 流程 +4. **Handler 分流模式已在 `_dispatch_reviews` 中验证可行**:L1379 `if handler: return []` 用 Handler 分流跳过 handler 项目的 PR Review 流程,TC Round Review 采用相同模式 ### 实现方案 @@ -599,7 +666,7 @@ def get_tc_subtask_summary(tc_db: Path, parent_issue: int) -> Optional[dict]: conn = get_connection(tc_db) try: parent_row = conn.execute( - "SELECT round_count FROM task_state WHERE issue_number = ?", + "SELECT round_count, status FROM task_state WHERE issue_number = ?", (parent_issue,) ).fetchone() if not parent_row: @@ -615,7 +682,7 @@ def get_tc_subtask_summary(tc_db: Path, parent_issue: int) -> Optional[dict]: summary = { "parent_id": str(parent_issue), - "parent_status": "done", # TC 路径 parent 是 Gitea Issue + "parent_status": parent_row["status"], # 从 task_state 读取实际状态 "round_count": parent_row["round_count"], "total": 0, "done": 0, "failed": 0, "cancelled": 0, "other": 0, } -- 2.45.4 From 3c58baa00f7c57a0dc332aba01f7571209ae40e1 Mon Sep 17 00:00:00 2001 From: claude_dev Date: Thu, 25 Jun 2026 08:39:44 +0800 Subject: [PATCH 4/5] chore: retrigger CI (paths-ignore fix #129 merged) -- 2.45.4 From 24600c38f9c880142a50c83972efe5b96ff4db99 Mon Sep 17 00:00:00 2001 From: claude_dev Date: Thu, 25 Jun 2026 18:15:11 +0800 Subject: [PATCH 5/5] chore: retrigger CI via synchronize -- 2.45.4