"""dispatch_from_branch 回归测试(CI 第二阶段 F841 证据)。 背景:host.py 的 dispatch_from_branch 里 `git commit` 的返回值 曾以 `c = git(...)` 绑定却从未读取(F841)。本轨只去绑定、 不删调用——提交本身是依赖派发的前置:依赖 worktree 的改动 必须先落到依赖分支,新 worktree 才从该分支建出、带血缘。 本测试用真实 git 仓库断言这条链: - 依赖 worktree 里未提交的改动被提交到依赖分支; - 新 worktree 的分支 host/ 由依赖分支 HEAD 派生 (含该提交),且任务记录进 state。 """ import os import shutil import subprocess import sys import tempfile import unittest import uuid from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(REPO_ROOT)) import host def _git(args, cwd, check=True): return subprocess.run(["git"] + args, cwd=cwd, check=check, capture_output=True, text=True) class FakeServer: def create_thread(self, path, model=None, sandbox=None, approval_policy=None): self.created = (path, model, sandbox, approval_policy) return "th-1" class DispatchFromBranchTest(unittest.TestCase): def test_commit_then_worktree_from_dep_branch(self): name = "dep-" + uuid.uuid4().hex[:8] server = FakeServer() state = {"tasks": {}} with tempfile.TemporaryDirectory() as tmp: repo = os.path.join(tmp, "repo") os.makedirs(repo) _git(["init", "-q", "-b", "main"], repo) _git(["config", "user.name", "Test"], repo) _git(["config", "user.email", "test@example.com"], repo) Path(repo, "f.txt").write_text("base\n", encoding="utf-8") _git(["add", "-A"], repo) _git(["commit", "-qm", "initial"], repo) _git(["branch", "dep-branch"], repo) dep_wt = os.path.join(tmp, "dep-wt") _git(["worktree", "add", "-q", dep_wt, "dep-branch"], repo) Path(dep_wt, "f.txt").write_text("dep change\n", encoding="utf-8") try: task, err = host.dispatch_from_branch( server, state, name, "do it", repo, "dep-branch", dep_wt, "model-x", "medium", "workspace-write", "never") self.assertEqual(err, "") self.assertIsNotNone(task) self.assertEqual(task["branch"], "host/" + name) self.assertEqual(task["thread_id"], "th-1") # 依赖分支 HEAD 含本次提交,且新分支由它派生。 dep_head = _git(["rev-parse", "dep-branch"], repo).stdout.strip() new_head = _git(["rev-parse", "host/" + name], repo).stdout.strip() self.assertEqual(new_head, dep_head) msg = _git(["log", "-1", "--format=%s", "dep-branch"], repo).stdout.strip() self.assertIn(name, msg) self.assertEqual( Path(task["cwd"], "f.txt").read_text(encoding="utf-8"), "dep change\n") self.assertIs(state["tasks"][name], task) finally: # 清理 HERE/work 下建出的 worktree(work/ 已 # gitignore,但不留给下一轮测试;须在临时仓 # 删除前用该仓的 git 登记移除)。 path = os.path.join(host.HERE, "work", "host-worktrees", name) if os.path.exists(path): _git(["worktree", "remove", "--force", path], repo, check=False) shutil.rmtree(path, ignore_errors=True) if __name__ == "__main__": unittest.main()