Files
Agentswarm/scripts/test-git-empty-repo.py
T
FastheiandClaude Opus 4.8 c38b5fd80d fix(agent/git): seed baseline commit for empty repos so results push back (#73)
Binding an empty repo (no commits) made `git clone` land an unborn HEAD with no
`origin/main`. `add_task_worktree` / `create_result_branch` then fell back to
base_ref=HEAD, and `git worktree add ... HEAD` failed with "invalid reference:
HEAD". The agent silently degraded to "executing on repo root without git push",
so the result branch never reached the remote.

Fix: after clone (and after refreshing an existing checkout), detect an empty
repo via `git rev-parse --verify HEAD` and plant one empty baseline commit on the
configured base branch. Worktree/branch creation and push then work normally and
the result branch is created on the (previously empty) remote.

Adds scripts/test-git-empty-repo.py covering the empty-repo path; existing
scripts/test-git-workflow.py still passes (no regression). Verified end-to-end
against a real empty GitHub repo: result branch pushed successfully.

Impact: Swarm agent git layer only (agent/git_operations.py). No change to
Manager<->Swarm contract, callbacks, billing, secret_ref, or audit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 22:35:27 +08:00

92 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""Regression for agent_swarm#73: binding an EMPTY repo (no commits) must still
create a task worktree and push results back, instead of silently degrading to
"without git push". See agent/git_operations.py:_ensure_baseline_commit.
"""
import asyncio
import importlib.util
import os
import subprocess
import sys
import tempfile
from pathlib import Path
from typing import Optional
ROOT = Path(__file__).resolve().parents[1]
GIT_OPS_PATH = ROOT / "agent" / "git_operations.py"
spec = importlib.util.spec_from_file_location("git_operations", GIT_OPS_PATH)
git_operations = importlib.util.module_from_spec(spec)
assert spec and spec.loader
spec.loader.exec_module(git_operations)
GitOperations = git_operations.GitOperations
def run(command: list[str], cwd: Optional[Path] = None):
result = subprocess.run(
command,
cwd=cwd,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)
if result.returncode != 0:
raise RuntimeError(
f"Command failed: {' '.join(command)}\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
)
return result
async def main():
with tempfile.TemporaryDirectory(prefix="swarm-git-empty-") as tmp:
tmpdir = Path(tmp)
remote = tmpdir / "remote.git"
workspace = tmpdir / "workspace"
worktree = tmpdir / "task-worktree"
# An EMPTY bare remote: no commits, no default branch — exactly the #73 scenario.
run(["git", "init", "--bare", "-b", "main", str(remote)])
os.environ["GIT_BASE_BRANCH"] = "main"
git_ops = GitOperations(str(workspace), "test-agent")
assert await git_ops.clone_workspace(str(remote)), "clone of empty repo failed"
assert await git_ops.is_git_workspace()
# Baseline must have been seeded so HEAD is now valid.
head = subprocess.run(
["git", "rev-parse", "--verify", "--quiet", "HEAD"],
cwd=workspace, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
)
assert head.returncode == 0 and head.stdout.strip(), "empty repo still has unborn HEAD"
# The previously-failing path: worktree add on an empty repo.
branch = await git_ops.add_task_worktree(str(worktree), "task-empty")
assert branch, "add_task_worktree returned None on empty repo (regression #73)"
# Do real work in the worktree and push it back.
task_git = GitOperations(str(worktree), "test-agent")
task_git.result_branch = branch
(worktree / "hello.py").write_text("print('hello from agent')\n", encoding="utf-8")
commit_sha = await task_git.commit_changes("Task task-empty: add hello")
assert commit_sha, "commit failed in empty-repo worktree"
pushed = await task_git.push_results()
assert pushed == branch, "push_results failed for empty-repo worktree"
refs = run(["git", "for-each-ref", "--format=%(refname:short)", "refs/heads"], cwd=remote)
assert branch in refs.stdout.splitlines(), "result branch missing on remote"
await git_ops.remove_task_worktree(str(worktree))
print("Empty-repo git workflow test passed")
print(f"branch={branch}")
print(f"commit={commit_sha}")
if __name__ == "__main__":
asyncio.run(main())