Compare commits

...

21 Commits

Author SHA1 Message Date
cfdaily b384433c80 [moz] docs(§21): fix 姜维 S1(同步 §20 task_state DDL)+ S2(SQL 语义变化说明)
CI / lint (pull_request) Successful in 26s
CI / test (pull_request) Successful in 33s
CI / frontend (pull_request) Successful in 14s
CI / notify-on-failure (pull_request) Successful in 0s
2026-06-20 18:59:18 +08:00
cfdaily dceddeff45 [moz] docs(§21): fix M1 §11/§12 表格截断 + 重编号 §18 不做的事
CI / lint (pull_request) Successful in 14s
CI / test (pull_request) Successful in 24s
CI / frontend (pull_request) Successful in 11s
CI / notify-on-failure (pull_request) Successful in 0s
2026-06-20 18:58:26 +08:00
cfdaily 31028a7e6f [moz] docs(§21): v2 补充 §12-§17 AI native 能力完整性
CI / lint (pull_request) Successful in 20s
CI / test (pull_request) Successful in 43s
CI / frontend (pull_request) Successful in 14s
CI / notify-on-failure (pull_request) Successful in 0s
§12: 14 项 AI native 能力对照表(全部可保留)
§13: Discussion 能力保留(DISCUSSION_PROMPT + Boids 四条不变)
§14: Agent 自建 sub Issue 模式(标题 [repo][sub][parent #N])
  - 替代 claim 竞争:各 agent 自建 sub + assign 自己
  - 重复不怕:庞统 round review 引导统一
§15: @mention 通知迁移(mention_queue 数据源改为 webhook)
§16: Round review 迁移(task_state parent_issue 映射)
§17: 无缝接续完整迁移(handoff/depends_on/retry/状态转换)
2026-06-20 18:53:31 +08:00
pangtong-fujunshi 6a73d6d6c7 Merge PR #97: [moz] docs(21): 统一工具链设计定稿
Deploy / ci (push) Failing after 7s
Deploy / deploy (push) Has been skipped
Deploy / notify-deploy-failure (push) Successful in 1s
Deploy / notify-deploy-success (push) Successful in 0s
2026-06-20 04:30:09 +00:00
cfdaily 576cd96b43 [moz] ci: 去掉 pip install --no-cache-dir,启用 pip 缓存加速 test job
CI / lint (pull_request) Successful in 17s
CI / test (pull_request) Successful in 2m34s
CI / frontend (pull_request) Successful in 23s
CI / notify-on-failure (pull_request) Successful in 0s
根因:--no-cache-dir 每次全量下载 8 个包(3 分钟),pytest 实际只跑 3 秒。
姜维确认安全去掉(commit f6f26d7 为排查 dist-info 问题添加,现已不适用)。
2026-06-20 12:04:35 +08:00
cfdaily 48c2b8ea3d [moz] docs(§21): 新增 §11 Issue closed 事件处理设计
CI / lint (pull_request) Successful in 18s
CI / test (pull_request) Successful in 9m35s
CI / frontend (pull_request) Successful in 14s
CI / notify-on-failure (pull_request) Successful in 0s
问题:Issue 被关闭时 daemon 不感知、创建者收不到通知
设计:_handle_issues 增加 action=closed 分支
  - 通知 Issue 创建者(非关闭者)
  - 纯通知类型(auto-pass)
  - 包含关闭者 + 修复摘要
2026-06-20 11:47:03 +08:00
cfdaily e89bd51d7c [moz] docs(§21): fix 姜维 S1-S4(模板关系说明 + ci_failure 终态修正 + action report 识别规范 + infrastructure 模板)
CI / lint (pull_request) Successful in 18s
CI / test (pull_request) Successful in 17m29s
CI / frontend (pull_request) Successful in 15s
CI / notify-on-failure (pull_request) Successful in 0s
2026-06-20 11:45:38 +08:00
cfdaily 4c3d125a30 [moz] docs(§21): fix S1 typo + S2 PriorContextSection 改造标注 + S3 CI status webhook 风险 + G1 refactor 测试命令 2026-06-20 11:45:38 +08:00
cfdaily 59499798ea [moz] docs(§21): Unified Toolchain Design — 统一工具链工作流
5 个割裂点统一解决:
1. agent API 统一到 Gitea(去掉黑板 API 引用)
2. steps 模板化(config/toolchain-templates.yaml)
3. issue_assigned 按 type/* 6 路分流(feature/impl/bug/docs/refactor/test)
4. action_type 和 business_type 分离
5. 完成检测改为终态事件 + 输出约束(output_template)

黑板无缝接续机制迁移:
- 前序产出 → Issue body Depends 引用
- handoff comment → Issue/PR comment
- 审查结果 → PR Review

L2 prompt section 重组(L0/L1/L3 不变)
2026-06-20 11:45:38 +08:00
jiangwei-infra 2febe54920 Merge PR #99: [moz] fix(ci): pin pydantic<2 for Python 3.9 compatibility
Deploy / ci (push) Failing after 8s
Deploy / deploy (push) Has been skipped
Deploy / notify-deploy-failure (push) Successful in 1s
Deploy / notify-deploy-success (push) Successful in 0s
2026-06-20 03:13:12 +00:00
cfdaily 59c95d4125 [moz] fix(ci): pin pydantic<2 for Python 3.9 compatibility
CI / lint (pull_request) Successful in 16s
CI / test (pull_request) Successful in 3m26s
CI / frontend (pull_request) Successful in 21s
CI / notify-on-failure (pull_request) Successful in 0s
CI runner 使用系统 Python 3.9.6,pydantic 2.x 需要 >=3.10。
不加约束时 pip 间歇性无法找到兼容版本(flaky)。
项目仅使用 BaseModel(1.x/2.x 均兼容),pin <2 安全。

Closes #98
2026-06-20 11:06:45 +08:00
pangtong-fujunshi 23d8691b3f Merge PR #96: [moz] docs(§20): Issue-Centric Orchestration
Deploy / ci (push) Failing after 13m25s
Deploy / deploy (push) Has been skipped
Deploy / notify-deploy-failure (push) Successful in 0s
Deploy / notify-deploy-success (push) Successful in 0s
2026-06-19 23:59:55 +00:00
cfdaily f4fea8f418 [moz] fix(ci): pip install 加 no_proxy=* 绕过系统代理白名单
CI / lint (pull_request) Successful in 18s
CI / test (pull_request) Successful in 13m40s
CI / frontend (pull_request) Successful in 15s
CI / notify-on-failure (pull_request) Successful in 0s
CI runner 继承了系统 Wi-Fi 代理(127.0.0.1:7890),代理是白名单机制,
pip install 走代理被拒绝。加 env no_proxy=* 让 pip 直连。
2026-06-20 07:44:37 +08:00
cfdaily 3a11327113 [moz] docs(§20): v2.2 纳入姜维 v2.0 Review S3(must_haves 改名影响面)
CI / lint (pull_request) Failing after 50s
CI / test (pull_request) Has been skipped
CI / frontend (pull_request) Has been skipped
CI / notify-on-failure (pull_request) Successful in 2s
2026-06-20 00:45:31 +08:00
cfdaily ddc5eb9897 [moz] docs(§20): v2.1 fix M1 dispatcher 直接SQL声明 + M2 Phase格式 + S1/S2 TaskAdapter残留清理
CI / lint (pull_request) Successful in 17s
CI / test (pull_request) Successful in 4m44s
CI / frontend (pull_request) Successful in 14s
CI / notify-on-failure (pull_request) Successful in 0s
2026-06-20 00:43:49 +08:00
cfdaily b0f4572ba6 [moz] docs(§20): v2.0 修订——Repository 模式换底 + Review 反馈整合
CI / lint (pull_request) Successful in 30s
CI / test (pull_request) Successful in 8m30s
CI / frontend (pull_request) Successful in 19s
CI / notify-on-failure (pull_request) Successful in 0s
庞统修正:
- 砍掉 task_index + TaskAdapter,改为 Repository 模式(Queries 类内部换底)
- 协作数据从 Gitea API 读,执行数据从 task_state 表读

司马懿 Review S1-S4 + G2:
- S1: 表数量 12→14 修正
- S2: P4 补充 action_report 识别方案(body 标记约定)
- S3: PR merge 关 Issue 需要 Closes #N commit message 约定
- S4: CI status webhook 验证放到 Phase 0
- G2: Mail 剩余职责明确

姜维 Review:
- task_state 加 issue_updated_at 缓存失效判断
- must_haves 改名 daemon_meta
2026-06-20 00:40:04 +08:00
cfdaily b6e58a164c docs(§20): 纳入姜维 Review 反馈 v1.1
CI / lint (pull_request) Successful in 13s
CI / test (pull_request) Successful in 28s
CI / frontend (pull_request) Successful in 12s
CI / notify-on-failure (pull_request) Successful in 0s
- task_index 加 issue_updated_at + issue_body_cache 字段
- must_haves 改名 daemon_meta(避免概念混淆)
- P2 推荐 TaskAdapter 替代 lazy load
- P4 补充 mention 解析适配层说明
- 新增 Phase 0 前置(webhook 权限)
- Phase 1 加 CI status webhook 验证
- 风险表补充 CI status 不触发 + 权限不足两项
2026-06-19 21:21:46 +08:00
cfdaily ea04b4c483 [moz] docs(§20): Issue-Centric Orchestration — Gitea Issue 替代黑板 DB 协作面
CI / lint (pull_request) Successful in 12s
CI / test (pull_request) Successful in 28s
CI / frontend (pull_request) Successful in 12s
CI / notify-on-failure (pull_request) Successful in 0s
设计目标:
1. 黑板 DB 协作面迁移到 Gitea Issue
2. 成果物以 Gitea 为基础(分支+PR)
3. webhook 部分替代 ticker
4. task 状态+spawner 逻辑不变
5. prompt 改造(黑板 API → Gitea API)

§3 目标架构:Gitea 做协作介质,daemon task_index 做轻量索引
§5 daemon 改造点:数据访问层(P1-P4)需讨论
§9 实施路径:5 个 Phase 分阶段实施
2026-06-19 21:05:17 +08:00
pangtong-fujunshi 04568108a6 Merge PR #95: [moz] impl(§17): issue_assigned steps git 操作具体化 + ToolchainApiSection Git 说明
Deploy / ci (push) Successful in 10s
Deploy / deploy (push) Successful in 11s
Deploy / notify-deploy-failure (push) Successful in 0s
Deploy / notify-deploy-success (push) Successful in 1s
2026-06-19 05:56:00 +00:00
cfdaily 6ea43d76e3 [moz] impl(§17): issue_assigned steps git 操作具体化 + ToolchainApiSection Git 说明
CI / lint (pull_request) Successful in 10s
CI / test (pull_request) Successful in 27s
CI / frontend (pull_request) Successful in 11s
CI / notify-on-failure (pull_request) Successful in 0s
改动 1: issue_assigned 编码路径 steps 改为具体 git 命令
  (checkout main → pull → checkout -b → add/commit → push)
改动 2: ToolchainApiSection 新增 Git 操作说明段落(含开发目录路径)
改动 3: 测试更新(issue_assigned 断言 + 3 个 Git 说明测试)
466 passed
2026-06-19 13:53:44 +08:00
pangtong-fujunshi 09520a414e Merge PR #94: [moz] docs(§17): issue_assigned steps git 操作具体化
Deploy / ci (push) Successful in 10s
Deploy / deploy (push) Successful in 11s
Deploy / notify-deploy-failure (push) Successful in 0s
Deploy / notify-deploy-success (push) Successful in 1s
2026-06-19 05:52:15 +00:00
7 changed files with 1250 additions and 5 deletions
+5 -1
View File
@@ -25,6 +25,8 @@ jobs:
- uses: actions/checkout@v4
- name: Setup Python
env:
no_proxy: "*"
run: |
rm -rf /tmp/ci-venv-lint
python3 -m venv /tmp/ci-venv-lint
@@ -43,11 +45,13 @@ jobs:
- uses: actions/checkout@v4
- name: Setup Python
env:
no_proxy: "*"
run: |
rm -rf /tmp/ci-venv-test
python3 -m venv /tmp/ci-venv-test
/tmp/ci-venv-test/bin/pip install --quiet --upgrade pip
/tmp/ci-venv-test/bin/pip install --quiet --no-cache-dir fastapi pydantic pyyaml uvicorn requests pytest pytest-asyncio httpx
/tmp/ci-venv-test/bin/pip install --quiet 'fastapi' 'pydantic<2' pyyaml uvicorn requests pytest pytest-asyncio httpx
- name: Debug environment
run: |
+2
View File
@@ -22,6 +22,8 @@ jobs:
- uses: actions/checkout@v4
- name: Setup Python
env:
no_proxy: "*"
run: |
python3 -m venv /tmp/ci-venv-deploy
/tmp/ci-venv-deploy/bin/pip install --quiet flake8 fastapi pydantic pyyaml uvicorn requests pytest pytest-asyncio httpx
@@ -0,0 +1,380 @@
---
title: "Issue-Centric Orchestration — Gitea Issue 替代黑板 DB 协作面"
created: 2026-06-19
version: v2.1 draft
status: draft
changelog: v2.1 修正 M1dispatcher 直接 SQL 声明)+ M2Phase 格式)+ S1/S2TaskAdapter 残留清理)
v2.0 纳入姜维+司马懿 Review 反馈 + 庞统 Repository 模式修正
v1.1 纳入姜维 Review 反馈
v1.0 初版
---
# Issue-Centric Orchestration
> **作者**: 庞统(副军师)🐦
> **日期**: 2026-06-19
> **定位**: 将黑板 DB 的协作面迁移到 Gitea Issuedaemon 逻辑保持不变
> **前置文档**: PRD-v3.0(共享意识空间)、§14 TaskTypeRegistry、§17 ToolchainHandler
---
## §1. 设计目标
| # | 目标 | 说明 |
|---|------|------|
| 1 | 黑板 DB 协作面迁移到 Gitea Issue | 需求、讨论、产出从黑板 DB 迁到 Issue |
| 2 | 成果物以 Gitea 为基础存放 | 分支 commit + PR |
| 3 | webhook 部分替代 ticker | 主动触发替代轮询,ticker 保留兜底 |
| 4 | task 状态 + spawner 逻辑不变 | daemon 内部状态机不变 |
| 5 | prompt 改造 | 黑板 API 引用改为 Gitea API,告知 agent 使用 Gitea 协作 |
**核心原则**: 只有数据存储位置变了(黑板 DB → Gitea Issue),daemon 的调度逻辑(dispatcher/ticker/spawner)不变。
---
## §2. 现状分析
### 2.1 黑板 DB 当前承担的角色
根据 PRD-v3.0,黑板是**共享意识空间**——所有 agent 通过它读写状态、感知变化、协调工作。
黑板 DB 包含 14 张表:
| 表 | 用途 | 分类 |
|---|------|------|
| tasks | 任务(标题、描述、状态、指派、retry) | 协作面 + 执行面 |
| comments | 讨论、@mention、action_report | 协作面 |
| outputs | 产出物(文本摘要、文件路径) | 协作面 |
| events | 事件流(SSE 推送) | 协作面 |
| reviews | 审查记录(verdict、round、consensus | 执行面 |
| checkpoints | 阶段审查(approve/reject | 执行面 |
| decisions | 决策记录 | 执行面 |
| observations | 风险观察 | 执行面 |
| experiences | 经验沉淀 | 执行面 |
| routing_decisions | 路由记录 | 执行面 |
| task_attempts | 重试历史 | 执行面 |
| mention_queue | @mention 队列 | 执行面 |
| experience_tags | 经验标签 | 执行面(§19 已标记废弃) |
| agents | Agent 注册信息 | 执行面 |
**协作面**tasks/comments/outputs/events= 迁移到 Gitea Issue
**执行面**reviews/checkpoints/decisions 等)= 保留在 daemon 内部
### 2.2 daemon 数据访问方式
当前 daemon 三个核心模块如何读写黑板 DB:
| 模块 | 读什么 | 怎么读 |
|------|-------|-------|
| ticker | pending task 列表 | `SELECT * FROM tasks WHERE status='pending'` (SQLite) |
| dispatcher | task 详情(title/description/must_haves | `Task.from_row(row)` 从 SQLite 行构建 |
| spawner | task 上下文构建 prompt | 从 task 对象的 title/description/must_haves 字段 |
| 模块 | 写什么 | 怎么写 |
|------|-------|-------|
| dispatcher | task statuspending→claimed→working | `UPDATE tasks SET status=?` |
| spawner | task statusworking→done/failed | `UPDATE tasks SET status=?` |
| handler | comment / output | `INSERT INTO comments/outputs` |
**关键发现**: daemon 大量依赖 `SELECT * FROM tasks WHERE status=?` 这种 SQL 查询来发现和调度 task。如果数据源迁到 Gitea Issue,这些查询的方式会变(从 SQLite 变为 Gitea API 或本地索引),但**查询的语义和返回的数据结构不变**。
---
## §3. 目标架构
### 3.1 分层
```
Gitea(协作介质,替代黑板 DB 协作面)
┌──────────────────────────────────────────┐
│ Issue #42: "实现功能 A" │
│ body: 需求描述 + 验收标准 │
│ assignee: zhangfei-dev │
│ labels: type/feat, priority/P2 │
│ comments: 讨论、@mention、进展汇报 │
│ │
│ 分支: fix/42-feature-a │
│ PR #43: fix/42 → main │
│ CI: lint + test │
│ Review: APPROVE / REQUEST_CHANGES │
│ → merge → Issue auto-close │
└──────────────────────────────────────────┘
↕ webhook(被动) ↕ API(主动)
┌──────────────────────────────────────────┐
│ daemon(执行引擎,内部状态管理不变) │
│ │
│ task_state(轻量索引,替代 tasks 表): │
│ issue_number → status, branch, retry │
│ │
│ 执行面表(不变): │
│ reviews, checkpoints, decisions, │
│ observations, experiences, │
│ routing_decisions, task_attempts │
│ │
│ 调度逻辑(不变): │
│ ticker → 扫 task_state → dispatch │
│ dispatcher → 读 Gitea Issue → spawn │
│ spawner → 读 Gitea Issue → prompt │
└──────────────────────────────────────────┘
```
### 3.2 数据映射
| 黑板 DB | Gitea 对应 | 迁移方式 |
|---------|-----------|---------|
| tasks.title | Issue.title | 直接对应 |
| tasks.description | Issue.body | 直接对应 |
| tasks.assignee | Issue.assignee | 直接对应 |
| tasks.status (pending/working/review/done) | daemon task_state 内部维护 | Issue open/closed 只表示生命周期 |
| tasks.priority | Issue label (priority/P0-P3) | label 模拟 |
| tasks.must_haves (JSON) | daemon task_state 内部存储 | daemon 专用元数据 |
| tasks.depends_on | Issue blocked_by | Gitea 原生 dependency |
| tasks.parent_task | Issue body 引用(如 `Parent: #42` | 约定 |
| tasks.retry_count / dispatch_count | daemon task_state 内部维护 | 执行面数据 |
| comments | Issue comment | 直接对应 |
| outputs | 分支 commit(代码/文档)+ Issue comment(摘要) | 成果物载体迁移 |
| events | webhook | 主动推送替代 SSE |
### 3.3 数据访问层改造:Repository 模式换底
**设计原则**:改造现有 RepositoryQueries/Blackboard 类)的实现,不新增中间层。
当前数据访问层已有 Repository 模式的基础——`Blackboard` 类和 `Queries` 类封装了所有数据访问,上层(dispatcher/spawner/ticker)通过方法调用(`queries.pending_dispatchable()``blackboard.get_task()`),不直接写 SQL。
改造做法:**Repository 接口不变,实现从 SQLite-only 改为 Gitea + SQLite。**
```
现在:
dispatcher → Queries(SQLite) → 黑板 DB
改造后:
dispatcher → Queries(Gitea-backed) → Gitea API(协作数据)+ SQLite(执行状态)
```
上层代码**基本**不用改。但有一个前置条件:dispatcher.py 中存在约 20 处直接操作 tasks 表的 SQL(绕过 Queries 类,如 `SELECT assignee FROM tasks``UPDATE tasks SET status=?`)。这些直接 SQL 需要先迁移到 Queries 方法调用,才能实现 Repository 换底。此项作为 Phase 1 的前置工作。
Queries 内部决定数据从哪来:
| 数据类型 | 来源 | 方式 |
|---------|------|------|
| title / description / assignee / labels | Gitea Issue | API 读取(webhook 触发时缓存) |
| comment / 讨论 | Gitea Issue comment | API 读取 |
| status / retry_count / dispatch_count | 本地 SQLite | 原有逻辑不变 |
| daemon_meta(原 must_haves | 本地 SQLite | 原有逻辑不变 |
**本地 SQLite 表(执行状态,Gitea 没有的)**
```sql
CREATE TABLE task_state (
issue_number INTEGER, -- Gitea Issue 编号
repo TEXT, -- 仓库名
status TEXT DEFAULT 'pending', -- daemon 内部状态机
retry_count INTEGER DEFAULT 0,
dispatch_count INTEGER DEFAULT 0,
max_retries INTEGER DEFAULT 2,
daemon_meta TEXT, -- JSONevent_type, steps 等 daemon 元数据)
issue_body_cache TEXT, -- 缓存的 Issue body(优化用,可失效重拉)
issue_updated_at TEXT, -- Gitea Issue 的 updated_at(缓存失效判断)
created_at TEXT,
updated_at TEXT,
PRIMARY KEY (issue_number, repo)
);
```
**为什么不用 TaskAdapter**Repository 内部已经把 Gitea 数据 + 本地执行状态合并成 Task 对象返回。上层调用 `queries.get_task(issue_number)` 得到的 Task 对象和现在一模一样——有 title、有 description、有 status。不需要额外 adapter 层。
---
## §4. 流程设计
### 4.1 创建 Task
```
① 庞统/主公在 Gitea 创建 Issue + 指派 agent
② Gitea webhook: issues/assigned
③ daemon toolchain handler 收到 webhook
→ 在 task_state 插入一行(issue_number, repo, assignee, status=pending
④ ticker 扫 task_state 发现 pending → dispatch → spawn agent
```
**和现在的区别**: 当前是庞统在黑板 API 创建 task。改造后是庞统在 Gitea 创建 Issuewebhook 自动触发 daemon 建索引。
### 4.2 执行 Task
```
① dispatcher 扫 task_state 发现 pending task
② spawner 从 Gitea API 读 Issue body(需求描述)
③ spawner 用 Issue body 构建 prompt(替代从黑板 DB 读 description
→ prompt 结构: Issue body(需求)+ PromptSection 注入(工作流程、约束、API 指引)
④ agent 收到 prompt → 执行
⑤ agent 在 Gitea Issue comment 汇报进展(替代黑板 comment)
⑥ daemon 更新 task_state status=working
```
### 4.3 审查
```
① agent 编码完成 → push 到分支 → 创建 PR
② Gitea webhook: pull_request/opened
→ daemon 更新 task_state status=review
③ Reviewer 在 Gitea 做 PR Review
④ Gitea webhook: pull_request_review
→ daemon 根据 Review 结果更新 task_state
⑤ Review 通过 → PR merge
→ Gitea 自动关闭 Issue
→ Gitea webhook: issues/closed
→ daemon 更新 task_state status=done
```
**commit message 约定**(司马懿 S3):Gitea PR merge 自动关闭 Issue 需要 commit message 包含 `Closes #N``Fixes #N` 关键词。agent 创建 PR 时在描述中加上此约定,确保 merge 后 Issue 自动关闭。
**审查统一走 PR Review**——不区分设计审查和代码审查,所有成果物都在分支上,Reviewer 一次性审。
### 4.4 CI 失败处理
```
① PR 创建 → CI 自动跑
② CI 失败 → Gitea webhook: pull_requestCI status
→ daemon toolchain handler 创建 ci_failure toolchain task
→ 指派给 PR 作者
→ agent 按 ci_failure steps 处理(已有逻辑,不变)
③ agent 修复 → push 到同分支 → PR 自动更新 → CI 重跑
```
### 4.5 ticker 兜底
webhook 可能丢失或延迟。ticker 保留原有逻辑,改为:
-`task_state` 中 status=pending 的记录(替代扫黑板 tasks 表)
-`task_state` 中 status=working 但超时的记录
- 如果发现 Gitea Issue 已 closed 但 task_state 还是 working → 更新为 done
---
## §5. daemon 需要改的地方
**原则: daemon 调度逻辑不变,只改数据访问层。**
### 5.1 数据访问层改造
| 模块 | 现在 | 改造后 | 影响范围 |
|------|------|-------|---------|
| `queries.pending_dispatchable()` | `SELECT * FROM tasks WHERE status='pending'` | `SELECT * FROM task_state WHERE status='pending'` | SQL 改表名 |
| `Task.from_row(row)` 构建 task 对象 | 从 tasks 表行直接取 title/description | 从 task_state 取 issue_number → 调 Gitea API 读 Issue title/body | 需要新增 Gitea API 调用 |
| `UPDATE tasks SET status=?` | 直接更新 SQLite | 更新 task_stateSQLite | SQL 改表名 |
| `INSERT INTO comments` | 写黑板 DB | 改为 Gitea Issue comment API | 需要新增 Gitea API 调用 |
### 5.2 ⚠️ 需要讨论的改造点
以下是因为数据源变了,daemon 实现需要调整的地方:
**P1: spawner 每次 spawn 都要调 Gitea API 读 Issue body**
当前 spawner 从 SQLite 读 task description(微秒级)。改为从 Gitea API 读(毫秒级,HTTP 请求)。
- **方案 A**: 每次 spawn 时实时调 Gitea API。简单但慢
- **方案 B**: webhook 触发时缓存 Issue body 到 `task_state.issue_body_cache` + `issue_updated_at`。spawn 时从缓存读
- **缓存失效**: spawner 构建 prompt 时比对 `issue_updated_at` 和 Gitea API 的 Issue `updated_at`,不匹配才重新拉取(正常情况 Issue body 创建后不改,开销可忽略)
- **推荐**: 方案 B + updated_at 校验
**P2: Repository 内部数据合并**
Queries 类内部改造——`get_task()` 方法从 Gitea API(或缓存)读 title/body,从 task_state 表读 status/retry_count,合并成 Task 对象返回。上层(dispatcher/spawner)无感知。
这是标准的 Repository 模式——接口不变(`get_task(id)` 返回 Task 对象),实现换底(从 SQLite 单源改为 Gitea + SQLite 双源)。不新增 TaskAdapter 类——Queries 类本身就是 Repository,内部合并是职责内的事。
**P3: agent 的 prompt 中引用黑板 API 的地方需要改**
PromptSection 中有多处 `POST localhost:8083/api/projects/.../tasks/.../comments`(黑板 API)。这些要改为 Gitea API
- `task_handler.py` TaskApiSection: `POST .../status``POST .../outputs` → 不需要(daemon 通过 webhook 自动感知状态)
- `toolchain_handler.py` ToolchainApiSection: `POST .../comments`action report)→ 改为 `POST Gitea API .../issues/.../comments`
- `toolchain_handler.py` ToolchainApiSection: `POST .../outputs` → 改为"push 到分支"指引
**P4: comments 表的 @mention 机制**
当前 @mention 通过黑板 `mention_queue` 表排队。改造后 @mention 通过 Gitea Issue/PR commentwebhook 自然触发)。但 mention_queue 的消费逻辑(ticker 扫描 → 通知 → agent 处理)需要适配。
- **方案**: mention_queue 保留,但数据来源从黑板 comment 改为 Gitea webhook payload
- **⚠️ 适配层**: Gitea webhook payload 中的 comment body 是完整 markdown 文本(不像黑板 comment 是结构化 JSON,有 comment_type、author 等字段)。mention_queue 消费侧需要适配:从 webhook payload 的 `comment.body` 中正则提取 @mention,而非 SQL WHERE 精确查询。这层适配在 Phase 4 mention 迁移时细化
- **影响**: ticker 中的 mention 处理逻辑需要适配
- **action_report 识别**(司马懿 S2):黑板 comment 有结构化 `comment_type=action_report` 字段,Gitea comment 只有 markdown body。迁移后用 **body 中的固定标记**识别,如 `<!-- action_report -->` 或约定 body 以 `[Action Report]` 开头。具体格式在 Phase 4 实施时确定。
---
## §6. prompt 改造
### 6.1 受影响的 PromptSection
| Section | 文件 | 当前内容 | 改造后 |
|---------|------|---------|-------|
| TaskApiSection | task_handler.py | 黑板 APIstatus 回写、outputs 提交) | 删除 status 回写(daemon 自动管);outputs 改为 git push |
| ToolchainApiSection | toolchain_handler.py | 黑板 APIaction_report comment、outputs | action_report 改为 Issue commentoutputs 改为 git push |
| TaskConstraintsSection | task_handler.py | "blackboard comment" 引用 | 改为 "Issue/PR comment" |
| ToolchainConstraintsSection | toolchain_handler.py | 已禁止 Mail | 同时告知 agent 使用 Gitea 协作 |
### 6.2 agent prompt 新增指引
agent 需要知道工作方式变了。新增一个通用 section(或加入现有 constraints section):
```
## 协作方式
- 你的任务通过 Gitea Issue 管理
- 需求描述在 Issue body 中
- 进展汇报通过 Issue comment
- 代码产出通过分支 commit + PR
- 审查通过 PR Review
- 不要使用黑板 API,不要使用 Mail API
```
---
## §7. Issue ↔ 分支 ↔ PR 关系
| 场景 | Issue : 分支 : PR |
|------|------------------|
| 简单任务(bugfix、小功能) | 1 : 1 : 1 |
| 复杂任务(多阶段) | 1 : 1 : N(分阶段提交 PR,同一个分支) |
| 极复杂任务(需拆解) | 1 : N : N(Issue body 列出子任务,每个一个分支+PR) |
**默认 1:1:1**。分支命名规范不变:`fix/{issue_number}-{brief}`
---
## §8. 不做的事
| 不做 | 理由 |
|------|------|
| 不做数据迁移 | 主公确认当前无正式使用数据 |
| 不做 Issue 状态 labelstatus/xxx | 中间状态 daemon 内部管,Issue 只有 open/closed |
| 不改 Mail | Mail 职责不变。Issue-centric 模式下 agent 不用 Mail(通过 Issue/PR comment 协作)。Mail 剩余职责:非 Gitea 相关的 agent 间点对点通知(如庞统通知赵云准备数据) |
| 不改前端(本阶段) | 前端改造独立于后端,后续设计 |
| 不做存量 task 退役 | 原 task 流程和 Issue 流程可共存,原 task 自然退役 |
| 不改 experiences/checkpoints/decisions 表 | 执行面表保留在 daemon,不受影响 |
---
## §9. 实施路径
| 阶段 | 内容 | 依赖 |
|------|------|------|
| 阶段 | 内容 | 依赖 |
|------|------|------|
| Phase 0 | webhook 权限配置(主公手动配)+ CI status webhook 验证(确认 Gitea CI status 变化触发 webhook | 设计 Review 通过 |
| Phase 1 | dispatcher.py 直接 SQL(约 20 处)迁移到 Queries 方法调用 + task_state 表创建 + Queries/Blackboard 内部改造(Gitea + SQLite 双源)+ must_haves→daemon_meta 重命名(17 文件 80 处,机械替换,需 CI 覆盖) | Phase 0 |
| Phase 2 | dispatcher/ticker 数据源从 tasks 表切换到 task_state | Phase 1 |
| Phase 3 | spawner 读 Issue body 构建 prompt(替代读黑板 description+ issue_updated_at 缓存失效机制 | Phase 2 |
| Phase 4 | prompt 改造(黑板 API → Gitea API+ mention_queue 适配(action_report body 标记约定) | Phase 3 |
| Phase 5 | 验证 + 清理废弃的黑板协作面表 | Phase 4 |
每个 Phase 独立可验证,出问题可以回退。
---
## §10. 风险评估
| 风险 | 等级 | 缓解 |
|------|------|------|
| Gitea API 不可用时 daemon 完全瘫痪 | 中 | webhook 触发时缓存 Issue body + issue_updated_at 校验(P1 方案 B),减少运行时 Gitea API 依赖 |
| Gitea webhook 丢失 | 低 | ticker 兜底扫描 |
| task_state 和 Gitea Issue 状态不一致 | 中 | ticker 定期校验(发现 Issue closed 但 index 未更新则修复) |
| spawner 性能下降(Gitea API 调用) | 低 | 方案 B 缓存 Issue bodyspawn 时不调 Gitea API |
| 原 task 流程和新 Issue 流程共存期混乱 | 中 | 可以限定只在特定项目中启用 Issue 模式,逐步切换 |
+820
View File
@@ -0,0 +1,820 @@
---
title: "Unified Toolchain Design — 统一工具链工作流设计"
created: 2026-06-20
version: v1.0 draft
status: draft
changelog: v1.0 初版
---
# Unified Toolchain Design
> **范围**: 仅 toolchain 流程。task 和 mail 不变。
> **前置**: §17 ToolchainHandler、§20 Issue-Centric Orchestration
> **目标**: 把割裂的单点优化整合为统一的 AI native 工作流
---
## §1. 背景与问题
### 1.1 当前割裂点
| # | 割裂 | 现状 | 本文解决 |
|---|------|------|---------|
| 1 | agent 同时引用两套 API | 黑板 APIaction report/outputs+ Gitea APIIssue/PR comment | §3 统一到 Gitea |
| 2 | steps 硬编码 | 9 处 `steps=[...]` 写死在 toolchain_routes.py | §4 模板化 |
| 3 | issue_assigned 只分 2 路 | 只按 infrastructure label 分流,不分 type/feat vs type/docs | §5 按 type/* 6 路分流 |
| 4 | action_type 和 business_type 混在一起 | issue_assigned 内部混入业务场景 | §6 分离两个维度 |
| 5 | verify 依赖黑板 comment_type | 迁移到 Gitea 后失效 | §7 重新定义完成检测 + 输出约束 |
### 1.2 黑板中的"无缝接续"设计(不能丢弃)
黑板 DB 中有多个机制确保后续 agent 可以无缝接续:
| 机制 | 当前实现 | 用途 | 迁移到 Issue 后 |
|------|---------|------|----------------|
| **任务描述** | tasks.title + tasks.description | agent 知道"做什么" | Issue title + body ✅ 直接对应 |
| **验收标准** | tasks.must_haves | agent 知道"怎样算完成" | Issue body 中结构化字段(模板定义)✅ |
| **前序产出** | outputs 表 + depends_on | agent 知道"之前做了什么" | Issue body 引用前序 Issue/PR(如 `Depends: #42`)⚠️ 需约定 |
| **handoff comment** | comment_type=handoff | agent 之间交接上下文(≥50 字符) | Issue/PR comment ✅ 天然支持 |
| **讨论历史** | comments 表 | agent 知道"讨论了什么" | Issue/PR comment 全部可读 ✅ |
| **审查结果** | reviews 表(verdict/round/consensus | agent 知道"审查结论" | PR ReviewAPPROVE/REQUEST_CHANGES+ Issue comment 记录 |
| **agent 声明式交接** | next_capability 字段 | agent 声明"我搞不定,需要 XX 能力的人" | Issue comment @对方(已有 mention 机制)✅ |
| **retry 历史** | task_attempts 表 | daemon 知道"试了几次" | daemon 内部 task_state 表 ✅ 不变 |
| **风险观察** | observations 表 | agent 标记"发现风险" | Issue comment(约定标记)⚠️ 需约定 |
**结论**:大部分机制可以自然迁移到 Issue。两个需要约定:
1. **前序产出引用**Issue body 中用 `Depends: #N``Parent: #N` 引用前序 Issue
2. **风险观察**Issue comment 中用约定标记(如 `⚠️ [观察]`
---
## §2. 设计原则
1. **Gitea only**toolchain agent 只操作 Gitea,不引用黑板 API、不用 Mail
2. **模板驱动**:steps 从硬编码改为模板,不同 Issue type 对应不同流程
3. **action_type 和 business_type 分离**:事件类型决定 action_hint,业务类型决定 steps
4. **完成检测:终态事件 + 输出约束**:用 Gitea 终态事件检测完成 + 结构化输出约束 agent 汇报
5. **无缝接续不丢弃**:handoff/前序产出/审查结果等机制迁移到 Issue 语义
---
## §3. 统一到 Gitea(割裂 1 解决)
### 3.1 agent API 引用变更
| 操作 | 现在(黑板 API | 改造后(Gitea API |
|------|----------------|-------------------|
| 提交 action report | `POST localhost:8083/.../comments` (comment_type=action_report) | `POST Gitea .../issues/{N}/comments`(结构化 body,§7 定义) |
| 提交产出 | `POST localhost:8083/.../outputs` | git push 到分支(代码/文档/测试) |
| 讨论 / @mention | `POST localhost:8083/.../comments` | `POST Gitea .../issues/{N}/comments` |
| 创建 PR | 已是 Gitea API(不变) | 不变 |
| 创建 Issue | 已是 Gitea API(不变) | 不变 |
### 3.2 agent prompt 中的 API 指引
ToolchainApiSection 改造——去掉所有 `localhost:8083` 引用,只保留 Gitea API
```
## 操作指令
### 汇报执行结果
执行完步骤后,在关联的 Issue 上 comment 汇报:
```bash
curl -X POST "{GITEA}/repos/{repo}/issues/{N}/comments" \
-H "Authorization: token <token>" \
-d '{"body": "[Action Report]\n\n**操作**...\n**结果**...\n**CI**..."}'
```
### 需要其他角色支持
在关联的 Issue/PR 上 comment @对方(已有机制)
### 代码产出
git push 到功能分支 → 创建 PR
```
---
## §4. steps 模板化(割裂 2 解决)
### 4.1 模板存储
steps 模板存放在 daemon 配置文件 `config/toolchain-templates.yaml`
```yaml
# 每种 business_type 对应一套 steps + output_template
issue_assigned:
feature:
steps:
- "理解需求(Issue body)→ 如有不明确在 Issue comment 追问"
- "git checkout main && git pull origin main"
- "git checkout -b fix/{issue_number}-{brief}"
- "编码实现 + 写 UT"
- "git add -A && git commit -m '[moz] feat: {title}' && git push"
- "创建 PRbody 引用 IssueCloses #{issue_number}"
- "等 CI + Review"
output_template: |
[Action Report]
**分支**fix/{issue_number}-{brief}
**PR**#{pr_number}
**改动文件**{files}
**CI**{ci_status}
impl:
steps:
- "读设计文档(Issue body 中的路径)→ 理解实现范围"
- "git checkout main && git pull origin main"
- "git checkout -b impl/{issue_number}-{brief}"
- "按设计编码实现 + 写 UT"
- "git add -A && git commit -m '[moz] impl: {title}' && git push"
- "创建 PRbody 引用 Issue + 设计文档路径)"
- "等 CI + Review"
output_template: |
[Action Report]
**设计文档**{design_doc}
**分支**impl/{issue_number}-{brief}
**PR**#{pr_number}
**改动文件**{files}
**CI**{ci_status}
bug:
steps:
- "读 Bug 描述 + 复现步骤(Issue body"
- "定位根因(读代码/日志,不要猜测)"
- "git checkout main && git pull origin main"
- "git checkout -b fix/{issue_number}-{brief}"
- "修复 + 写回归测试"
- "git add -A && git commit -m '[moz] fix: {title}' && git push"
- "创建 PRbody 说明根因和修复方式)"
- "等 CI + Review"
output_template: |
[Action Report]
**根因**{root_cause}
**修复方式**{fix_approach}
**分支**fix/{issue_number}-{brief}
**PR**#{pr_number}
**CI**{ci_status}
docs:
steps:
- "读文档目标(Issue body"
- "git checkout main && git pull origin main"
- "git checkout -b docs/{issue_number}-{brief}"
- "编写文档到 docs/ 对应目录"
- "git add -A && git commit -m '[moz] docs: {title}' && git push"
- "创建 PR"
- "等 Review"
output_template: |
[Action Report]
**文档路径**{doc_path}
**分支**docs/{issue_number}-{brief}
**PR**#{pr_number}
refactor:
steps:
- "读重构目标 + 影响范围(Issue body"
- "git checkout main && git pull origin main"
- "git checkout -b refactor/{issue_number}-{brief}"
- "重构 + 确保现有测试不 breakpython -m pytest tests/unit/ -q"
- "git add -A && git commit -m '[moz] refactor: {title}' && git push"
- "创建 PR(body 说明重构内容和影响范围)"
- "等 CI + Review"
output_template: |
[Action Report]
**重构范围**{scope}
**测试结果**{test_result} passed
**分支**refactor/{issue_number}-{brief}
**PR**#{pr_number}
**CI**{ci_status}
test:
steps:
- "读测试目标(Issue body"
- "git checkout main && git pull origin main"
- "git checkout -b test/{issue_number}-{brief}"
- "编写测试脚本到 tests/ 对应目录"
- "运行测试验证(python -m pytest {test_file} -v"
- "git add -A && git commit -m '[moz] test: {title}' && git push"
- "创建 PR"
- "等 CI + Review"
output_template: |
[Action Report]
**测试文件**{test_file}
**测试结果**{test_result}
**分支**test/{issue_number}-{brief}
**PR**#{pr_number}
infrastructure:
steps:
- "根据 Issue body 中的错误来源和日志片段排查问题"
- "修复基础设施问题(CI runner/网络/Gitea/磁盘等)"
- "修复后在 Issue 上 comment 说明修复方式和结果"
- "汇报执行结果"
output_template: |
[Action Report]
**问题**{problem}
**根因**{root_cause}
**修复方式**{fix}
**验证**{verification}
# toolchain 事件(非 issue_assigned)的模板
ci_failure:
steps:
- "查看 CI 日志(PR 页面或 Gitea Actions"
- "判断失败原因:a.代码问题→修复→push b.基础设施→创建 Issue 指派 jiangwei-infra"
- "汇报结果"
output_template: |
[Action Report]
**原因类型**{cause_type}
**操作**{action}
**CI 重跑**{ci_status}
# ... 其他 toolchain 事件(review_result/review_request/...)各自定义
```
### 4.2 模板加载
daemon 启动时加载 YAML 配置,运行时按 `action_type + business_type` 查找模板:
```python
def get_steps(action_type: str, business_type: str = "") -> list[str]:
"""从模板配置获取 steps"""
section = TEMPLATES.get(action_type, {})
if isinstance(section, dict) and business_type:
return section.get(business_type, {}).get("steps", section.get("default", {}).get("steps", []))
return section.get("steps", [])
```
---
## §5. 按 type/* 6 路分流(割裂 3 解决)
### 5.1 issue_assigned handler 改造
当前只按 `infrastructure` label 分 2 路。改为按 `type/*` label 分流:
```python
# 伪代码
labels_list = [lbl.get("name", "") for lbl in (issue.get("labels") or [])]
# 1. 基础设施(不变)
if any("infrastructure" in lbl.lower() for lbl in labels_list):
business_type = "infrastructure"
# 2. 按 type/* 确定 business_type
elif "type/feat" in labels_list:
business_type = "feature"
elif "type/impl" in labels_list:
business_type = "impl"
elif "type/bug" in labels_list:
business_type = "bug"
elif "type/docs" in labels_list:
business_type = "docs"
elif "type/refactor" in labels_list:
business_type = "refactor"
elif "type/test" in labels_list:
business_type = "test"
else:
business_type = "feature" # 默认走编码流程
# 从模板获取 steps + output_template
template = get_template("issue_assigned", business_type)
steps = template["steps"]
output_template = template["output_template"]
```
### 5.2 action_hint 差异化
当前 action_hint 按 action_type 固定("你收到一个 Issue 指派...")。改为同时体现 business_type
```python
_ACTION_HINTS = {
"issue_assigned": {
"feature": "你收到一个功能需求,理解需求后编码实现。",
"impl": "你收到一个实现任务,按设计文档编码实现。",
"bug": "你收到一个 Bug 报告,定位根因后修复。",
"docs": "你收到一个文档任务,编写文档。",
"refactor": "你收到一个重构任务,重构并确保测试通过。",
"test": "你收到一个测试任务,编写测试脚本。",
"infrastructure": "你收到一个基础设施问题报告,请排查并修复。",
},
"ci_failure": "你收到一个 CI 失败通知,这是一个需要你修复失败测试的事件。",
"review_result": "你收到一个 Review 结果通知,这是一个需要你执行动作的事件。",
# ... 其他 action_type 不分 business_type
}
```
---
## §6. action_type 和 business_type 分离(割裂 4 解决)
### 6.1 两个维度
| 维度 | 来源 | 决定什么 | 示例 |
|------|------|---------|------|
| **action_type** | webhook 事件类型 | action_hint"你收到一个 XX 通知" | ci_failure / review_result / issue_assigned |
| **business_type** | Issue label type/* | steps + output_template | feature / impl / bug / docs / refactor / test |
### 6.2 组合规则
- issue_assignedaction_type=issue_assigned + business_type 从 label 确定
- ci_failureaction_type=ci_failure(无 business_typeCI 失败就是 CI 失败)
- review_resultaction_type=review_result(无 business_typeReview 就是 Review
- 只有 issue_assigned 需要 business_type 维度(因为同一个 action_type 下不同业务的流程不同)
---
## §7. 完成检测 + 输出约束(割裂 5 解决)
### 7.1 设计原则
| 关注点 | 方案 |
|--------|------|
| 完成检测 | Gitea 终态事件优先 + Issue comment 兜底 |
| 输出约束 | 按 business_type 定义的 output_template(不是空泛的"简要描述" |
### 7.2 完成检测:按 action_type 分类
| action_type | 终态信号 | 检测方式 | 兜底 |
|-------------|---------|---------|------|
| issue_assigned | PR merged 或 Issue closed | webhook: pull_request/closed(merged=true) 或 issues/closed | — |
| ci_failure | agent Issue comment 汇报 | Issue comment 检测([Action Report] 标记) | ⚠️ Gitea 1.26.2 不触发 CI status webhook,只能靠 comment 兜底。ticker 可选轮询 Gitea commit status API 作为补充 |
| review_result(APPROVED) | PR merged | webhook: pull_request/closed(merged=true) | — |
| review_result(CHANGES) | agent push 到分支 | webhook: pull_request/synchronize | — |
| review_request | Review 提交 | webhook: pull_request_review | — |
| review_updated | Review 提交 | webhook: pull_request_review | — |
| review_comment | agent comment | webhook: issue_comment/created | — |
| mention | agent comment | webhook: issue_comment/created | — |
| deploy_failure | agent Issue comment 汇报 | Issue comment 检测 | ✅ |
| infrastructure_failure | agent Issue comment 汇报 | Issue comment 检测 | ✅ |
| review_merged | — | auto-pass | — |
### 7.3 状态流转:单一终态保证
一个 task 只有一个终态触发。daemon 内部状态机保证:
```
pending → working → done(终态事件触发,只触发一次)
→ failed(超时/异常)
```
终态事件到来时检查 task 当前状态:
- 如果已经 done/failed → 忽略(幂等)
- 如果 working → 标 done
- 如果 pending → 异常,记日志
**中间事件**push/comment/Review submitted**不改变 task 状态**——它们是过程中的信号,不是终态。
### 7.4 输出约束:output_template
每种 business_type 有自己的 output_template(§4.1 定义)。agent 完成后在 Issue comment 中按模板汇报。
daemon 的 verify 通过 webhook 事件检测终态(不需要检查 comment 内容)。但 output_template 的价值是**约束 agent 的汇报质量**——不是检测完成用的,而是给后续 agent/审查者提供结构化信息。
output_template 作为 steps 的最后一步注入 prompt
### 7.5 action report 识别规范
daemon 通过 webhook `issue_comment/created` 感知到新 comment 后,需要判断是否为 action report。
**匹配规则**
- 精确匹配:comment body 以 `[Action Report]` 开头(允许前导空白)
- 容错策略:如果 body 包含 `[Action Report]`(不要求开头),也接受
- 大小写不敏感
**匹配失败处理**
- 不匹配的 comment 不触发完成检测
- 作为普通讨论 comment 处理(agent 之间的 handoff/讨论)
```
最后一步:汇报执行结果,在 Issue 上 comment,格式:
[Action Report]
**根因**<根因描述>
**修复方式**<做了什么>
**分支**fix/42-xxx
**PR**#43
**CI**:✅ 通过
```
---
## §8. 无缝接续机制迁移
### 8.1 前序产出引用
当前黑板用 `depends_on` 字段 + `PriorOutputsSection` 注入前序产出摘要。
迁移到 Issue 后,在 Issue body 中用约定引用:
```markdown
## 依赖
Depends: #42(前序任务)
Parent: #40(父 Issue
## 前序产出摘要
- #42 完成了数据获取模块(分支 fix/42-dataPR #43
- 数据路径:/Volumes/stock/xxx
```
agent 读 Issue body 自然获得前序上下文。daemon 的 spawner 在构建 prompt 时,可以解析 Issue body 中的 `Depends: #N`,调 Gitea API 读取前序 Issue 的 comment(包含 action report)作为上下文注入。
### 8.2 handoff comment
当前 handoff 通过黑板 `comment_type=handoff` + ≥50 字符约束。
迁移到 Issue 后,agent 的 handoff 就是**Issue/PR comment**。不需要 comment_type 字段——所有有实质内容的 comment 都是为后续 agent 提供上下文的"handoff"。
### 8.3 审查结果
当前黑板 reviews 表存 verdict/round/consensus。
迁移后:
- **代码审查**PR ReviewGitea 原生,APPROVE/REQUEST_CHANGES
- **方案审查**(设计 PR):同上
- **庞统 round review**:保留在 daemon 内部(不迁移,这是编排逻辑)
---
## §9. prompt 层级(L0-L4 不变,L2 重组)
| 层 | 内容 | 不变? |
|---|------|-------|
| L0 铁律 | 安全底线 | ✅ 不变 |
| L1 角色 | SOUL.md / IDENTITY.md | ✅ 不变 |
| L2 引擎注入 | **本文重组** | 改造 |
| L3 被参考 | Skill 列表 | ✅ 不变 |
L2 重组后的 section 列表:
| priority | Section | 内容 | 来源 |
|----------|---------|------|------|
| 10 | ToolchainContextSection | action_hint + Issue body(需求)+ steps | 改造:从模板加载 steps |
| 20 | PriorContextSection | 前序产出(解析 Issue body 中的 Depends | 改造现有 PriorOutputsSection |
| 30 | RoleSkillSection | 角色 Skill | 不变 |
| 35 | GitOperationSection | Git 操作说明(PR #95 已有) | 不变 |
| 40 | GiteaApiSection | Gitea API 指引(Issue comment + PR 创建) | 改造:去掉黑板 API |
| 50 | ToolchainConstraintsSection | 约束 + Red Flags | 不变 |
| 55 | GiteaConventionSection | Gitea 标题规范 | 不变 |
| 60 | WikiGuideSection | 知识查询引导 | 不变 |
| 65 | DeliveryChecklistSection | 交付检查 | 改造:output_template 替代空泛的"简要描述" |
---
## §10. 涉及改动
| 文件 | 改动 | 工作量 |
|------|------|-------|
| `config/toolchain-templates.yaml` | 新建:6 种 business_type steps + output_template | 新文件 |
| `src/daemon/toolchain_handler.py` | ToolchainApiSection 改为 GiteaApiSection(去黑板 API);action_hint 支持 business_type | 改造 |
| `src/daemon/toolchain_handler.py` | verify_completion 改为终态事件检测 + Issue comment 兜底 | 改造 |
| `src/api/toolchain_routes.py` | issue_assigned handler 按 type/* 6 路分流 | 改造 |
| `src/api/toolchain_routes.py` | steps 从模板加载(替代硬编码) | 改造 |
| `src/daemon/toolchain_handler.py` | webhook handler 增加终态事件检测 | 新增 |
| `.gitea/ISSUE_TEMPLATE/` | 新增 impl.yml / docs.yml / refactor.yml | 新文件 |
| `tests/` | 更新测试 | 改造 |
---
## §11. Issue closed 事件处理
### 11.1 问题
当前 `_handle_issues` 只处理 `action == "assigned"`,不处理 `action == "closed"`。Issue 被关闭时:
- daemon 不感知(webhook `issues/closed` 被忽略)
- 创建者 / 关注者收不到通知
- 如果该 Issue 对应一个活跃的 taskdaemon 不知道 Issue 已关闭
### 11.2 设计
`_handle_issues` 增加 `action == "closed"` 分支:
**谁被通知**Issue 创建者(`issue.user.login`)。
**通知内容**(通过 toolchain task 发给创建者):
- Issue 标题 + 编号
- 关闭者(`payload.sender.login`
- 关闭时间
- Issue 上最后一个 comment 的摘要(修复说明)
**通知类型**:纯通知(event_type=issue_closedverify auto-pass,和 review_merged 一样)。
**特殊情况**
- 如果关闭者是创建者自己(自己关自己创建的),不通知(避免自环)
- 如果 Issue 没有创建者信息或创建者不是已知 agent,跳过
### 11.3 实现伪代码
```python
# _handle_issues 中新增
if action == "closed":
issue_creator = issue.get("user", {}).get("login", "")
closed_by = payload.get("sender", {}).get("login", "")
# 自己关自己创建的,不通知
if issue_creator == closed_by:
return
# 只通知已注册的 agent
if issue_creator not in AGENT_IDS:
return
# 读取最后一个 comment 作为修复摘要
comments = issue.get("comments", 0)
last_comment_summary = "(无 comment)"
# 可选:调 Gitea API 读最后一个 comment
title = f"Issue 已关闭: {issue_title} ({repo}#{issue_number})"
description = f"Issue {repo}#{issue_number} 已被 {closed_by} 关闭。\n\n{last_comment_summary}"
_send_toolchain_task(
to_agent=issue_creator,
title=title,
description=description,
event_type="issue_closed",
action_type="issue_closed",
steps=[], # 纯通知,无步骤
context_data={
"issue_number": issue_number,
"repo": repo,
"issue_title": issue_title,
"closed_by": closed_by,
},
)
```
### 11.4 _ACTION_HINTS 新增
```python
"issue_closed": "你创建的 Issue 已被关闭。这是一条纯通知,阅读即可。",
```
### 11.5 EVENT_LABELS_ZH 新增
```python
"issue_closed": "Issue 已关闭",
```
### 11.6 verify_completion
issue_closed 走 auto-pass(和 review_merged 一样),纯通知不需要 agent 动作。
### 11.7 涉及改动
| 文件 | 改动 |
|------|------|
| `src/api/toolchain_routes.py` `_handle_issues` | 新增 `action == "closed"` 分支 |
| `src/daemon/toolchain_handler.py` `_ACTION_HINTS` | 新增 issue_closed |
| `src/daemon/toolchain_handler.py` `EVENT_LABELS_ZH` | 新增 issue_closed |
| `src/daemon/toolchain_handler.py` `verify_completion` | issue_closed auto-pass |
| `templates/toolchain/issue_closed.md` | 新建通知模板 |
| `tests/` | 新增 closed 事件测试 |
---
## §12. AI Native 能力完整性(v2 补充)
> 本节确保 Gitea 替代黑板后,PRD-v3.0 的 AI native 能力不降级。
> 对照 §01 四相循环实现 + spawner.py / ticker.py / operations.py 的实际代码逐项检查。
### 12.1 完整能力对照表
| # | AI native 能力 | 黑板实现 | Gitea Issue 对应 | 保留? | §21 补充 |
|---|-------------|---------|-----------------|-------|---------|
| 1 | **Discussion 讨论** | spawner DISCUSSION_PROMPT_TEMPLATE + spawn_type=discussion + ticker 广播 | parent Issue 创建后 ticker 广播 spawn discussion promptIssue comment 是讨论空间 | ✅ | §13 |
| 2 | **Agent 自建 sub** | agent POST /tasks {parent_task, must_haves} | agent POST Gitea /issues {title: "[repo][sub][parent #N] ..."} + assign 自己 | ✅ | §14 |
| 3 | **@mention 通知** | comment mentions 字段 → mention_queue → ticker 扫描 → spawn 被@者 | Gitea Issue/PR comment @ → webhook issue_comment → daemon 解析 @ → spawn | ✅ | §15 |
| 4 | **Round review** | _check_round_complete → parent sub 全终态 → spawn 庞统三问 | daemon 扫 task_state parent 下所有 sub 终态 → spawn 庞统 review | ✅ | §16 |
| 5 | **Retry 上下文** | retry_count 字段 + _build_retry_context | task_state.retry_count + retry prompt(不变) | ✅ | 不需额外 |
| 6 | **Handoff comment** | comment_type=handoff + ≥50 字符 | Issue/PR comment(无 type 区分,但 Boids 行为准则约束 agent 写实质内容) | ✅ | §17 |
| 7 | **Outputs 产出物** | outputs 表 {agent, type, content_path, summary} | 分支 commit(代码/文档)+ Issue comment(摘要) | ✅ | 不需额外 |
| 8 | **Depends_on 前序** | tasks.depends_on 字段 + PriorOutputsSection | Issue body `Depends: #N` + daemon 解析注入 | ✅ | §17 |
| 9 | **Boids 行为准则** | DISCUSSION_PROMPT 中的 4 条准则 | discussion prompt 不变(Boids 准则在 prompt 中,与存储介质无关) | ✅ | §13 |
| 10 | **Agent 自主涌现** | 无 assignee 的 parent task → ticker 广播 → agent 自主讨论/创建 sub | 无 assignee 的 parent Issue → ticker 广播 discussion → agent 自建 sub Issue | ✅ | §13/§14 |
| 11 | **Guardrail 安全红线** | dispatcher check_task → violations → block | 不变(guardrail 查 task_state,不依赖黑板) | ✅ | 不需额外 |
| 12 | **Classify outcome** | spawner _classify_outcome → done/failed/pending | 不变(classify 逻辑在 daemon 内部) | ✅ | 不需额外 |
| 13 | **Rebuttal** | review.py submit_rebuttal → @mention assignee + 回到 working | PR Review REQUEST_CHANGES → webhook → daemon 通知 agent | ✅ | 不需额外 |
| 14 | **Checkpoints** | checkpoint_routes.py → approve/reject | PR Review(代码/方案审查统一走 PR Review) | ✅ | 不需额外 |
### 12.2 结论
**14 项 AI native 能力全部可保留。** 其中需要补充设计的是 5 项(§13-§17),其余沿用现有 daemon 逻辑不变。
---
## §13. Discussion 能力保留
### 13.1 设计
庞统创建 parent Issue(无 assignee)后,触发 discussion
```
庞统创建 parent Issue → webhook: issues/assigned(或 ticker 发现 pending 无 assignee
→ daemon 检测:无 assignee = 广播讨论
→ ticker 广播 spawn 所有 agentspawn_type=discussion
→ 每个 agent 收到 DISCUSSION_PROMPT_TEMPLATE
```
agent 收到的 discussion prompt(与现有 spawner.py DISCUSSION_PROMPT_TEMPLATE 一致):
```
你被 spawn 来参与 Gitea Issue 讨论。这是一个四相循环的讨论环节。
## 你的任务
{parent Issue body — goal}
## Issue APIGitea
你可以随时:
- 读 Issue 详情+commentsGET /repos/{repo}/issues/{N}
- 写 commentPOST /repos/{repo}/issues/{N}/comments
body: {"body": "内容(@agent-id 自动路由)"}
- 创建 sub IssuePOST /repos/{repo}/issues
title: "[repo][sub][parent #{N}] 任务名"
body: "Parent: #{N}\n## 任务\n..."
assignees: ["自己"]
## 行为准则
1. 你是自主的。读 Issue、思考、行动,不要等指令。
2. 不重复别人的工作。动手前先读 Issue comments 看谁在做什么(Separation)。
3. 保持方向对齐。你的产出方向和 parent goal 对齐,不确定时 @pangtong-fujunshiAlignment)。
4. 产出可共享。产出写入 Issue comment,让其他人能看到你的成果(Cohesion)。
5. 不越界。安全红线不要碰,超出能力的 @ 庞统升级(Boundary)。
6. 随时讨论。执行过程中需要协作时 @ 对应 Agent,讨论是灵活的不是固定阶段的。
## 讨论完成后
- 如果讨论收敛到可执行的任务,直接创建 sub Issue
- 如果有分歧或不确定,在 Issue 上 comment @ 庞统裁决
- 如果庞统在 parent Issue 中 @ 了你认领特定任务,优先响应
```
**与现有实现差异**API 从黑板 APIlocalhost:8083)改为 Gitea API。行为准则(Boids 四条)完全不变。
### 13.2 庞统初始引导
庞统创建 parent Issue 时,可以在 Issue body 或 comment 中 @ 特定 agent 引导认领:
```
Issue body:
## 任务
做一个双均线量化策略...
## 建议分工
@zhangfei-dev 你来认领策略编码
@zhaoyun-data 你来认领数据准备
其他需要补充的请大家自主认领
```
但庞统不需要知道全部——关羽可能发现需要风控,自己创建 sub 去做。司马懿可能发现需要测试,自己创建 sub。**这是涌现,不是分配。**
---
## §14. Agent 自建 sub Issue 模式
### 14.1 设计
替代当前的 claim 竞争模式。每个 agent 自己创建 sub Issue + assign 自己。
**标题格式**
```
[quant][sub][parent #100] 策略编码
```
- `[quant]` — 项目代号
- `[sub]` — 标记为 sub Issue
- `[parent #100]` — parent Issue 编号(Gitea 自动渲染 #100 为链接)
- 后面是人类可读的任务描述
**Sub Issue body**
```markdown
Parent: #100
## 任务
从 parent Issue 继承的具体任务描述
## 验收标准
...
```
Gitea 的 Issue reference 功能会自动在 parent Issue #100 上显示 "Referenced by #101"。
### 14.2 不需要 claim 竞争
当前黑板的 claim 模式(CAS 原子操作)是为了防止两个 agent 认领同一个 task。
在 Gitea Issue 模式下,每个 agent 创建自己的 sub Issue——**不存在竞争**。各创建各的,各 assign 各的。
**重复不怕**:如果关羽和张飞创建了内容重叠的 sub Issue,庞统在 round review 时引导两人统一看法。这不是错误,是讨论的契机。**不做严谨工作流,做 AI 生态。**
### 14.3 daemon 内部 parent/sub 映射
daemon 维护 parent/sub 层级(用于 round review 检测):
```sql
-- task_state 表(§20 设计)
CREATE TABLE task_state (
issue_number INTEGER,
repo TEXT,
parent_issue INTEGER, -- 新增:parent Issue 编号
status TEXT DEFAULT 'pending',
...
);
```
daemon 监听 Issue 创建 webhook → 解析标题中的 `[parent #N]` → 记录 parent_issue。
⚠️ **需同步更新 §20 task_state DDL** 新增 `parent_issue INTEGER` 列。
---
## §15. @mention 通知迁移
### 15.1 当前实现
```
agent 写 comment → mentions 字段 → mention_queue 表 → ticker 扫描 → spawn 被@者
```
### 15.2 Gitea 迁移
```
agent 写 Issue/PR comment → webhook: issue_comment/created → daemon 解析 @ → spawn 被@者
```
**mention_queue 表保留**,但数据来源改为 webhook payload。daemon 收到 issue_comment webhook 后:
1. 正则提取 comment body 中的 `@(
[-
]*)`
2. 写入 mention_queue
3. ticker 消费 mention_queue → spawn 被@者
复用现有 mention_utils.py 的 extract_mentions 逻辑(§25 已实现)。
---
## §16. Round Review 迁移
### 16.1 当前实现
ticker._check_round_complete
1. 扫描所有 parent task
2. 检查 sub task 是否全部终态
3. spawn 庞统 review(三问框架)
### 16.2 Gitea 迁移
ticker._check_round_complete 改为:
1. 扫描 task_state 中 parent_issue IS NOT NULL(替代原 `SELECT DISTINCT parent_task FROM tasks`,语义不变——都是找有子任务的 parent)
2. 找到所有 parent_issue 相同的 sub Issue
3. 检查 sub Issue 是否全部终态(通过 task_state.status
4. 全部终态 → spawn 庞统 review
**庞统三问 prompt 不变**
```
1. Goal 还清晰吗?(是否有 goal drift)
2. 成果物覆盖 goal 了吗?(逐条检查验收标准)
3. 下一轮需要做什么?(创建新 sub / 标记完成 / 调整方向)
```
庞统通过 Gitea API 读 parent Issue bodygoal+ 所有 sub Issue 的 comments/outputs → 做评估。
---
## §17. 无缝接续机制完整迁移
### 17.1 handoff comment
当前:comment_type=handoff + ≥50 字符
Gitea:所有 Issue/PR comment 都是天然的 handoff。Boids 行为准则约束 agent 写实质内容。不强制 ≥50 字符——用 Boids 准则引导比硬阈值更 AI native。
### 17.2 depends_on 前序引用
当前:tasks.depends_on 字段 + PriorOutputsSection
GiteaIssue body 中 `Depends: #N`。daemon 解析 → 读前序 Issue 的 comments(含 Action Report)→ 注入 prompt。
### 17.3 retry 上下文
currentretry_count 字段 + _build_retry_context
Giteatask_state.retry_count(不变,daemon 内部)。retry prompt 中加入前序 Issue/PR 链接供 agent 参考。
### 17.4 状态转换流转
agent 完成后的状态转换(working → review → done)全部由 daemon 内部管理。agent 不需要手动 POST status。daemon 通过以下信号自动感知:
| 信号 | 来源 | 触发 |
|------|------|------|
| agent 创建 sub Issue | webhook: issues/opened | sub status=pending → dispatch |
| agent 创建 PR | webhook: pull_request/opened | sub status → review |
| PR Review 提交 | webhook: pull_request_review | review 结果 → done 或 back to working |
| PR merge | webhook: pull_request/closed(merged) | sub status → done + Issue auto-close |
| agent Issue comment | webhook: issue_comment/created | 检查是否为 Action Report → 终态检测 |
---
## §18. 不做的事
| 不做 | 理由 |
|------|------|
| 不改 task handler | task 流程不变(§20 设计中的 task 逐步迁移到 Issue 是后续工作) |
| 不改 mail | mail 职责不变 |
| 不改 dispatcher/ticker 核心逻辑 | 调度逻辑不变(§20 Phase 1 的 dispatcher SQL 迁移是前置工作) |
| 不做前端改造 | 后续独立设计 |
| 不改 experiences/checkpoints/decisions 表 | 执行面表保留在 daemon |
+3 -3
View File
@@ -1032,10 +1032,10 @@ async def _handle_issues(payload: Dict[str, Any]) -> None:
event_type="issue_assigned",
action_type="issue_assigned",
steps=[
f"创建分支 fix/{issue_number}-{brief}",
f"在开发目录执行 git 操作:\n a. git checkout main && git pull origin main\n b. git checkout -b fix/{issue_number}-{brief}",
"编码 + 写 UT",
"push → 等 CI",
f"CI 通过后创建 PRGitea API: POST /repos/{repo}/pulls",
f"git add -A && git commit -m \"[moz] fix: {issue_title[:30]}\" && git push origin fix/{issue_number}-{brief}",
f"CI 通过后创建 PRGitea API: POST /repos/{repo}/pullshead: fix/{issue_number}-{brief}, base: main",
"等 Review",
"提交 action reportPOST http://localhost:8083/api/projects/_toolchain/tasks/<task_id>/commentscomment_type=action_report",
],
+14
View File
@@ -179,6 +179,20 @@ class ToolchainApiSection:
"⚠️ Issue body 必须包含错误来源链接(PR/Commit + CI run),让排查者能直接看到全貌。",
"⚠️ label 数字 ID 先 GET /repos/{repo}/labels 查询 type/infrastructure 对应的 ID。",
"",
"### Git 操作说明",
"",
"你的工作目录是开发目录(如 ~/.openclaw/sanguo_projects/sanguo_moziplus_v2/)。",
"标准分支操作流程:",
"```bash",
"git checkout main && git pull origin main # 从最新主干开始",
"git checkout -b fix/{branch_name} # 创建功能分支",
"# ... 写代码 ...",
"git add -A && git commit -m 'message' # 提交改动",
"git push origin {branch_name} # 推送到远程",
"```",
"",
"⚠️ 不要在 main 分支上直接 commit。",
"",
]
return "\n".join(lines)
+26 -1
View File
@@ -563,7 +563,7 @@ class TestIssueAssignedLabelRouting:
def test_normal_issue_keeps_coding_steps(self):
source_file = PROJECT_ROOT / "src" / "api" / "toolchain_routes.py"
source = source_file.read_text()
assert '创建分支 fix/' in source
assert 'git checkout -b fix/' in source
assert 'issue_assigned' in source
@@ -593,3 +593,28 @@ class TestRedFlagsInfrastructure:
source = source_file.read_text()
assert "不是我代码的问题" in source
assert "基础设施问题" in source
class TestGitOperationGuidance:
"""ToolchainApiSection should include Git operation guidance."""
def test_has_git_operation_section(self):
source_file = PROJECT_ROOT / "src" / "daemon" / "toolchain_handler.py"
source = source_file.read_text()
assert "Git 操作说明" in source
assert "git checkout main" in source
assert "git pull origin main" in source
assert "git checkout -b" in source
def test_has_no_main_commit_warning(self):
source_file = PROJECT_ROOT / "src" / "daemon" / "toolchain_handler.py"
source = source_file.read_text()
assert "不要在 main 分支上直接 commit" in source
def test_issue_assigned_steps_have_git_commands(self):
source_file = PROJECT_ROOT / "src" / "api" / "toolchain_routes.py"
source = source_file.read_text()
assert 'git checkout main && git pull origin main' in source
assert 'git checkout -b fix/' in source
assert 'git add -A && git commit' in source
assert 'git push origin fix/' in source