From bebdbbf9c52dc510c9f712073e64046706f54d53 Mon Sep 17 00:00:00 2001 From: jinsongwang Date: Sat, 30 May 2026 05:52:10 +0800 Subject: [PATCH 1/2] =?UTF-8?q?refactor:=20=E6=8B=86=E5=88=86=20run=5Fsubt?= =?UTF-8?q?ask=20=E4=B8=BA=E5=A4=9A=E4=B8=AA=E8=BE=85=E5=8A=A9=E5=87=BD?= =?UTF-8?q?=E6=95=B0=20(Issue=20#3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 拆分为: _create_worktree(), _build_task_md(), _run_claude(), _verify_changes(), _generate_context() Co-Authored-By: Claude Opus 4.8 --- agent_go/executor.py | 196 +++++++++++++++++++++++++++++-------------- 1 file changed, 132 insertions(+), 64 deletions(-) diff --git a/agent_go/executor.py b/agent_go/executor.py index c9f2adf..b80ae7e 100644 --- a/agent_go/executor.py +++ b/agent_go/executor.py @@ -12,17 +12,13 @@ __all__ = ["run_subtask"] -def run_subtask(task_id, subtask, repo, task_dir, logger, upstream_worktrees=None, headless=False, issue_ref="", active_pids=None, active_pids_lock=None): - sub_id = subtask["id"] + +def _create_worktree(task_id, sub_id, repo, task_dir, logger): + """Create worktree for a subtask. Returns (worktree_path, create_time_ms).""" sub_dir = task_dir / sub_id sub_dir.mkdir(parents=True, exist_ok=True) worktree = sub_dir / "work" - logger.info(f"─── {sub_id} START: {subtask['title']} ───") - log_event(logger, "subtask_start", {"id": sub_id, "title": subtask["title"], - "depends_on": subtask.get("depends_on", []), "headless": headless, "issue": issue_ref}) - - clone_start = time.time() worktree_create_ms = 0 if (worktree / ".git").exists(): logger.info(f"worktree 已存在,跳过创建") @@ -44,30 +40,11 @@ def run_subtask(task_id, subtask, repo, task_dir, logger, upstream_worktrees=Non worktree.mkdir(parents=True, exist_ok=True) shutil.copytree(str(repo), str(worktree), dirs_exist_ok=True) - # 产物传递:通过 git merge 将上游代码合并到当前 worktree - # Tag 使用完整路径 task_id/sub_id 避免跨任务冲突 - merge_conflicts = {} - merge_results = [] - merge_upstream_ms = 0 - if upstream_worktrees: - for up_id, up_path in upstream_worktrees.items(): - if up_path.exists(): - upstream_tag = f"{task_id}/{up_id}" - logger.info(f"产物传递 (git merge): {up_id} → {sub_id} (tag={upstream_tag})") - m_start = time.time() - _git_merge_upstream(up_path, worktree, upstream_tag, logger, headless=headless) - merge_upstream_ms += (time.time() - m_start) * 1000 - # 检测上游 merge 是否产生冲突 - conflict_file = worktree / ".MERGE_CONFLICT" - has_conflict = conflict_file.exists() - if has_conflict: - merge_conflicts[up_id] = conflict_file.read_text(encoding="utf-8") - conflict_file.unlink() - merge_results.append(collect_merge_result(up_id, not has_conflict, - merge_conflicts.get(up_id, "").split("\n") if has_conflict else None)) - clone_time = time.time() - clone_start + return worktree, worktree_create_ms + - # 构建 TASK.md:包含完整 Agent Prompt、资源清单、上游上下文 +def _build_task_md(subtask, repo, task_dir, worktree, logger, headless, upstream_worktrees=None, merge_conflicts=None): + """Build TASK.md content. Returns (task_md, verification, skill_names, unresolved_skills).""" task_md_parts = [f"# 子任务: {subtask['title']}", ""] # 注入 git merge 冲突信息(如有) @@ -140,7 +117,6 @@ def run_subtask(task_id, subtask, repo, task_dir, logger, upstream_worktrees=Non logger.warning(f"Skill 未找到: \"{sn}\",已跳过。已安装: {installed_names[:10]}") # 将 Agent Prompt 中的源项目路径替换为 worktree 路径,确保隔离 - # 边界字符包含: 空白、引号、括号、冒号(含全角)、路径分隔符、中文标点 task_md_text = "\n".join(task_md_parts) _boundary_chars = r'\s"\'\(\):/:,。、' _before = rf'(? Date: Sat, 30 May 2026 08:45:52 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20run=5Fsubtask?= =?UTF-8?q?=20=E9=87=8D=E6=9E=84=E7=9A=84=204=20=E4=B8=AA=20review=20?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. _verify_changes 添加 issue_ref 参数,调用方传入(修复 commit 消息丢失 issue 引用) 2. 移除 _verify_changes 的 has_changes 死代码参数 3. 保存原始 verification 值传给 _generate_context(修复路径重写问题) 4. 移除 _build_task_md 未使用的 upstream_worktrees 参数 Co-Authored-By: Claude Opus 4.8 --- agent_go/executor.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/agent_go/executor.py b/agent_go/executor.py index b80ae7e..8ceacd9 100644 --- a/agent_go/executor.py +++ b/agent_go/executor.py @@ -43,7 +43,7 @@ def _create_worktree(task_id, sub_id, repo, task_dir, logger): return worktree, worktree_create_ms -def _build_task_md(subtask, repo, task_dir, worktree, logger, headless, upstream_worktrees=None, merge_conflicts=None): +def _build_task_md(subtask, repo, task_dir, worktree, logger, headless, merge_conflicts=None): """Build TASK.md content. Returns (task_md, verification, skill_names, unresolved_skills).""" task_md_parts = [f"# 子任务: {subtask['title']}", ""] @@ -163,8 +163,8 @@ def _run_claude(task_md, worktree, env, headless, agent, sub_id, active_pids, ac return result, sandbox_type, claude_time -def _verify_changes(task_id, subtask, worktree, has_changes, headless, task_md, env, tag_name, - active_pids, active_pids_lock, logger): +def _verify_changes(task_id, subtask, worktree, headless, task_md, env, tag_name, + active_pids, active_pids_lock, logger, issue_ref=""): """Verify changes, commit if needed, run verification commands. Returns verification dict.""" # 记录变更摘要(使用 git status --porcelain 检测所有变更,包括新文件) status_result = subprocess.run(["git", "status", "--porcelain"], cwd=str(worktree), capture_output=True, text=True) @@ -193,7 +193,6 @@ def _verify_changes(task_id, subtask, worktree, has_changes, headless, task_md, # Git 提交 + tag(Conventional Commits 格式),供下游子任务 merge # Tag 包含 task_id 前缀,避免跨任务冲突 git_start = time.time() - issue_ref = "" # Not available in this context, will be handled by caller if needed if has_changes: commit_msg = _format_commit(subtask['title'], issue_ref, subtask["id"]) add_result = subprocess.run(["git", "add", "-A"], cwd=str(worktree), capture_output=True) @@ -381,12 +380,14 @@ def run_subtask(task_id, subtask, repo, task_dir, logger, upstream_worktrees=Non # 3. Build TASK.md task_md, verification, skill_names, unresolved_skills = _build_task_md( subtask, repo, task_dir, worktree, logger, headless, - upstream_worktrees=upstream_worktrees, merge_conflicts=merge_conflicts + merge_conflicts=merge_conflicts ) # Write TASK.md to disk (sub_dir / "TASK.md").write_text(task_md, encoding="utf-8") + # Save original verification before path rewriting (for context.md) + original_verification = verification # Rewrite verification command paths if verification and str(repo) in verification: _boundary_chars = r'\s"\'\(\):/:,。、' @@ -422,8 +423,8 @@ def run_subtask(task_id, subtask, repo, task_dir, logger, upstream_worktrees=Non # 6. Verify changes tag_name = f"{task_id}/{sub_id}" verify_results = _verify_changes( - task_id, subtask, worktree, True, headless, task_md, env, tag_name, - active_pids, active_pids_lock, logger + task_id, subtask, worktree, headless, task_md, env, tag_name, + active_pids, active_pids_lock, logger, issue_ref=issue_ref ) has_changes = verify_results["has_changes"] summary = verify_results["summary"] @@ -434,8 +435,8 @@ def run_subtask(task_id, subtask, repo, task_dir, logger, upstream_worktrees=Non retry_count = verify_results["retry_count"] verification_results = verify_results["verification_results"] - # 7. Generate context - _generate_context(subtask, task_dir, sub_id, logger, headless, result, verify_ok, summary, verification) + # 7. Generate context (use original verification, not path-rewritten) + _generate_context(subtask, task_dir, sub_id, logger, headless, result, verify_ok, summary, original_verification) # 状态判定: completed(有变更) / no_changes(完成但无变更) / failed(异常) if result.returncode == 0 and verify_ok: