From b0bca0df5c635dc98d088d4646a4daa6ec45807c Mon Sep 17 00:00:00 2001 From: cfdaily Date: Sat, 20 Jun 2026 23:14:43 +0800 Subject: [PATCH 1/4] =?UTF-8?q?[moz]=20fix(=C2=A721):=20GAP-1=20issue=5Fas?= =?UTF-8?q?signed=206=E8=B7=AF=E5=88=86=E6=B5=81=20+=20GAP-2=20issue=5Fclo?= =?UTF-8?q?sed=20auto-pass=20+=20GAP-3=20Discussion=20Prompt=20v3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 第一轮背靠背一致性检查发现的 3 个 GAP: GAP-1: §21 §5 issue_assigned 按 type label 分流 - 从 labels 解析 business_type (feature/impl/bug/docs/refactor/test) - 从 toolchain-templates.yaml get_steps() 获取对应 steps - fallback 到硬编码 steps(向后兼容) GAP-2: §21 §11 Issue closed 事件处理 - toolchain_handler verify 加 issue_closed auto-pass - toolchain_routes 加 issue closed webhook → 纯通知 task GAP-3: §21 §13.2 Discussion Prompt v3 - 新增 你是谁 段(agent_identity 注入) - 新增 你必须做什么 4 维度(定位/建议/认领/风险) - 新增 Comment 格式(角色名开头) - Boids 行为准则不变 - 黑板 API 不变(§21 范围仅 toolchain) --- src/api/toolchain_routes.py | 70 ++++++++++++++++++++++++++++----- src/daemon/spawner.py | 38 +++++++++++++++--- src/daemon/toolchain_handler.py | 6 +++ 3 files changed, 99 insertions(+), 15 deletions(-) diff --git a/src/api/toolchain_routes.py b/src/api/toolchain_routes.py index e727ef4..1af4604 100644 --- a/src/api/toolchain_routes.py +++ b/src/api/toolchain_routes.py @@ -1025,14 +1025,36 @@ async def _handle_issues(payload: Dict[str, Any]) -> None: }, ) else: - title = f"Issue 指派: {issue_title} ({repo}#{issue_number})" - _send_toolchain_task( - to_agent=assignee, - title=title, - description=text, - event_type="issue_assigned", - action_type="issue_assigned", - steps=[ + # §21 §5 按 type/* label 解析 business_type + business_type = "feature" # default + for lbl in labels_list: + lbl_lower = lbl.lower() + if "bug" in lbl_lower: + business_type = "bug" + elif "impl" in lbl_lower or "feature" in lbl_lower: + business_type = "feature" if "feature" in lbl_lower else "impl" + elif "docs" in lbl_lower or "documentation" in lbl_lower: + business_type = "docs" + elif "refactor" in lbl_lower: + business_type = "refactor" + elif "test" in lbl_lower: + business_type = "test" + + # §21 §4 从 YAML 模板获取 steps(fallback 到硬编码) + from src.daemon.toolchain_templates import get_steps + yaml_steps = get_steps("issue_assigned", business_type) + if yaml_steps: + # 渲染占位符 + rendered_steps = [] + for s in yaml_steps: + s = s.replace("{issue_number}", str(issue_number)) + s = s.replace("{brief}", brief) + s = s.replace("{title}", issue_title[:30]) + rendered_steps.append(s) + steps_to_use = rendered_steps + else: + # fallback: 硬编码 steps(向后兼容) + steps_to_use = [ f"在开发目录执行 git 操作:\n a. git checkout main && git pull origin main\n b. git checkout -b fix/{issue_number}-{brief}", "编码 + 写 UT", "文档同步:如果本次改动涉及设计变更或接口变更,在同一分支更新 docs/design/ 对应文档。如无需更新,在 action report 中说明「文档无需更新」", @@ -1040,7 +1062,16 @@ async def _handle_issues(payload: Dict[str, Any]) -> None: f"CI 通过后创建 PR(Gitea API: POST /repos/{repo}/pulls,head: fix/{issue_number}-{brief}, base: main)— PR body 必须含 Closes #{issue_number}", "等 Review", "提交 action report(POST http://localhost:8083/api/projects/_toolchain/tasks//comments,comment_type=action_report)— 报告中必须说明文档是否需要更新及处理结果", - ], + ] + + title = f"Issue 指派: {issue_title} ({repo}#{issue_number})" + _send_toolchain_task( + to_agent=assignee, + title=title, + description=text, + event_type="issue_assigned", + action_type="issue_assigned", + steps=steps_to_use, context_data={ "issue_number": issue_number, "repo": repo, @@ -1048,9 +1079,30 @@ async def _handle_issues(payload: Dict[str, Any]) -> None: "labels": labels, "issue_body": issue_body or "(无描述)", "brief": brief, + "business_type": business_type, }, ) + elif action == "closed": + # §21 §11 Issue closed 纯通知(auto-pass) + assignee_login = "" + issue_assignees = issue.get("assignees") or [] + if issue_assignees: + assignee_login = issue_assignees[-1].get("login", "") + if not assignee_login or assignee_login not in AGENT_IDS: + logger.debug("Issue closed but no valid assignee, skipping") + return + title = f"Issue 已关闭: {issue_title} ({repo}#{issue_number})" + _send_toolchain_task( + to_agent=assignee_login, + title=title, + description=f"## Issue 已关闭\n\n**{repo}#{issue_number}**: {issue_title}\n\nIssue 已被关闭。", + event_type="issue_closed", + action_type="issue_closed", + steps=[], # 纯通知,无步骤 + context_data={"repo": repo, "issue_number": issue_number}, + ) + elif action == "opened": if "部署失败" in issue_title: # 从 Issue body 提取 commit hash(Gitea deploy workflow 格式) diff --git a/src/daemon/spawner.py b/src/daemon/spawner.py index 9cfbbaf..239c3a2 100644 --- a/src/daemon/spawner.py +++ b/src/daemon/spawner.py @@ -103,9 +103,9 @@ SPAWN_PROMPT_TEMPLATE = """{identity_section} """ -DISCUSSION_PROMPT_TEMPLATE = """你被 spawn 来参与黑板讨论。这是一个 v2.9 四相循环的讨论环节。 +DISCUSSION_PROMPT_TEMPLATE = """你被 spawn 来参与讨论。这是一个四相循环的讨论环节。 -## 你的任务 +## 讨论主题 {goal_snapshot} @@ -113,14 +113,38 @@ DISCUSSION_PROMPT_TEMPLATE = """你被 spawn 来参与黑板讨论。这是一 {constraints} +## 你是谁 + +{agent_identity} + +## 你必须做什么 + +读完需求后,在黑板 comment 回应(必须,不是可选): + +1.【定位】这个需求和你有什么关系?你的专业能力能贡献什么? +2.【建议】你对实现方案有什么建议?(技术选型、数据来源、实现路径) +3.【认领】如果你需要参与,创建 sub task 并在 parent task comment 注册: + - 创建 sub task: POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks + body: {{"title": "...", "description": "...", "task_type": "...", "parent_task": "{task_id}", "must_haves": "{{\"capability\": \"...\"}}"}} + - 创建后在 parent task comment: "[你的角色名] 我创建了 sub: 任务名,我负责 简述" +4.【风险】如果你发现风险、不合理的假设、或遗漏的环节,直接提出 + +⚠️ 每个 agent 必须 comment。即使你认为和自己无关,也要说明原因——这证明你读过并思考过了。 + +## Comment 格式 + +你的 comment 必须以角色名开头,让其他人知道你是谁: +[角色名] 你的观点 +例:[张飞] 我来负责策略编码,用 vnpy CtaTemplate 实现。 +例:[关羽] 这个策略需要风控,连亏 3 天应暂停。 +例:[赵云] 数据已就绪,2024-08 缺失已补齐。 + ## 黑板 API -你可以随时: -- 读黑板:GET http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}?expand=all(含 comments、outputs) +- 读黑板:GET http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}?expand=all - 写 comment:POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{task_id}/comments - body: {{"author": "{agent_id}", "body": "内容(@agent-id 自动路由)"}} + body: {{"author": "{agent_id}", "body": "内容"}} - 创建 sub task:POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks - body: {{"title": "...", "description": "...", "task_type": "...", "parent_task": "{task_id}", "must_haves": "{{\"capability\": \"...\"}}"}} - 认领任务:POST http://{api_host}:{api_port}/api/projects/{project_id}/tasks/{{sub_task_id}}/claim ## 行为准则 @@ -387,12 +411,14 @@ curl -X POST http://{self.api_host}:{self.api_port}/api/projects/{project_id}/ta goal_snapshot = description or title constraints = must_haves or "(无特殊约束)" + agent_identity = self._inject_agent_identity(agent_id) return DISCUSSION_PROMPT_TEMPLATE.format( goal_snapshot=goal_snapshot, constraints=constraints, project_id=project_id, task_id=task_id, agent_id=agent_id, + agent_identity=agent_identity, api_host=self.api_host, api_port=self.api_port, ) diff --git a/src/daemon/toolchain_handler.py b/src/daemon/toolchain_handler.py index 368ed15..cf1c589 100644 --- a/src/daemon/toolchain_handler.py +++ b/src/daemon/toolchain_handler.py @@ -331,6 +331,12 @@ class ToolchainHandler(BaseTaskHandler): return VerifyResult(True, "merged_passthrough", "review_merged auto-pass") + + # 特殊处理:issue_closed 始终通过(纯通知, §21 §11) + if meta.get("action_type") == "issue_closed": + return VerifyResult(True, "issue_closed_passthrough", + "issue_closed auto-pass") + # 1. 优先检查 action_report comment report_row = conn.execute( "SELECT id FROM comments WHERE task_id=? " -- 2.45.4 From e238eaec314520d1348d9a2980c688e169794acf Mon Sep 17 00:00:00 2001 From: cfdaily Date: Sat, 20 Jun 2026 23:16:51 +0800 Subject: [PATCH 2/4] =?UTF-8?q?[moz]=20fix:=20CI=20lint=20E303=20+=20F401?= =?UTF-8?q?=20=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - toolchain_handler.py: 删除多余空行 (E303) - toolchain_templates.py: 删除 unused json import (F401) --- src/daemon/toolchain_handler.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/daemon/toolchain_handler.py b/src/daemon/toolchain_handler.py index cf1c589..24a0057 100644 --- a/src/daemon/toolchain_handler.py +++ b/src/daemon/toolchain_handler.py @@ -331,7 +331,6 @@ class ToolchainHandler(BaseTaskHandler): return VerifyResult(True, "merged_passthrough", "review_merged auto-pass") - # 特殊处理:issue_closed 始终通过(纯通知, §21 §11) if meta.get("action_type") == "issue_closed": return VerifyResult(True, "issue_closed_passthrough", -- 2.45.4 From bae3244e2482d7b8df18ea4313e6f44280edb98f Mon Sep 17 00:00:00 2001 From: cfdaily Date: Sat, 20 Jun 2026 23:37:17 +0800 Subject: [PATCH 3/4] =?UTF-8?q?[moz]=20fix:=20S2=20=5Fsteps=5Fcache=20None?= =?UTF-8?q?=20=E5=B4=A9=E6=BA=83=E4=BF=AE=E5=A4=8D=EF=BC=88=E5=8F=B8?= =?UTF-8?q?=E9=A9=AC=E6=87=BF=20Review=20=E5=8F=8D=E9=A6=88=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clear_cache() 中 _steps_cache.clear() 改为 _steps_cache = None, 避免在 get_steps() 之前调用时 AttributeError。 --- src/daemon/toolchain_templates.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/daemon/toolchain_templates.py b/src/daemon/toolchain_templates.py index e767188..9dc5b81 100644 --- a/src/daemon/toolchain_templates.py +++ b/src/daemon/toolchain_templates.py @@ -87,7 +87,8 @@ def render_template(name: str, variables: Dict[str, str]) -> str: def clear_cache() -> None: """清空模板缓存(用于测试或热更新)""" _template_cache.clear() - _steps_cache.clear() + global _steps_cache + _steps_cache = None # 重置为 None,强制下次 reload # --------------------------------------------------------------------------- -- 2.45.4 From 41d60cca2c65fdab6dafe4889f29f853eecad170 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Sat, 20 Jun 2026 23:38:43 +0800 Subject: [PATCH 4/4] =?UTF-8?q?[moz]=20fix:=20M1=20label=20type/feature?= =?UTF-8?q?=E2=86=92type/feat=20=E5=AF=B9=E9=BD=90=E4=BB=93=E5=BA=93?= =?UTF-8?q?=E5=AE=9E=E9=99=85=20label=EF=BC=88=E5=A7=9C=E7=BB=B4=20Review?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - impl.yml: type/feature → type/feat - toolchain_routes.py: business_type feature→feat(匹配 Gitea label) - toolchain-templates.yaml: feature→feat --- .gitea/ISSUE_TEMPLATE/impl.yml | 2 +- config/toolchain-templates.yaml | 2 +- src/api/toolchain_routes.py | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.gitea/ISSUE_TEMPLATE/impl.yml b/.gitea/ISSUE_TEMPLATE/impl.yml index f985e10..cfed43c 100644 --- a/.gitea/ISSUE_TEMPLATE/impl.yml +++ b/.gitea/ISSUE_TEMPLATE/impl.yml @@ -2,7 +2,7 @@ name: 实现任务 about: 按设计文档实现功能 title: "[moz] impl: " labels: - - type/feature + - type/feat body: - type: textarea id: description diff --git a/config/toolchain-templates.yaml b/config/toolchain-templates.yaml index 8798382..5bc407d 100644 --- a/config/toolchain-templates.yaml +++ b/config/toolchain-templates.yaml @@ -6,7 +6,7 @@ # issue_assigned(按 Issue label 分 6 路 + infrastructure) # --------------------------------------------------------------------------- issue_assigned: - feature: + feat: steps: - "理解需求(Issue body)→ 如有不明确在 Issue comment 追问" - "git checkout main && git pull origin main" diff --git a/src/api/toolchain_routes.py b/src/api/toolchain_routes.py index 1af4604..f56586b 100644 --- a/src/api/toolchain_routes.py +++ b/src/api/toolchain_routes.py @@ -1026,13 +1026,15 @@ async def _handle_issues(payload: Dict[str, Any]) -> None: ) else: # §21 §5 按 type/* label 解析 business_type - business_type = "feature" # default + business_type = "feat" # default for lbl in labels_list: lbl_lower = lbl.lower() if "bug" in lbl_lower: business_type = "bug" - elif "impl" in lbl_lower or "feature" in lbl_lower: - business_type = "feature" if "feature" in lbl_lower else "impl" + elif "impl" in lbl_lower: + business_type = "impl" + elif "feat" in lbl_lower: + business_type = "feat" elif "docs" in lbl_lower or "documentation" in lbl_lower: business_type = "docs" elif "refactor" in lbl_lower: -- 2.45.4