diff --git a/pkg/agentic/auto_pr.go b/pkg/agentic/auto_pr.go index 5faf715..cd8da71 100644 --- a/pkg/agentic/auto_pr.go +++ b/pkg/agentic/auto_pr.go @@ -79,6 +79,8 @@ func (s *PrepSubsystem) autoCreatePR(workspaceDir string) { return } + s.cleanupForgeBranch(ctx, repoDir, forgeRemote, workspaceStatus.Branch) + if result := ReadStatusResult(workspaceDir); result.OK { workspaceStatusUpdate, ok := workspaceStatusValue(result) if !ok { @@ -106,6 +108,21 @@ func (s *PrepSubsystem) buildAutoPRBody(workspaceStatus *WorkspaceStatus, commit return b.String() } +// cleanupForgeBranch removes an agent branch from the Forge remote after the PR is published. +// +// s.cleanupForgeBranch(context.Background(), "/workspace/repo", "ssh://git@forge.lthn.ai:2223/core/go-io.git", "agent/fix-tests") +func (s *PrepSubsystem) cleanupForgeBranch(ctx context.Context, repoDir, remote, branch string) bool { + if repoDir == "" || remote == "" || branch == "" { + return false + } + if s == nil || s.ServiceRuntime == nil { + return false + } + + result := s.Core().Process().RunIn(ctx, repoDir, "git", "push", remote, "--delete", branch) + return result.OK +} + func truncate(s string, max int) string { if len(s) <= max { return s diff --git a/pkg/agentic/auto_pr_test.go b/pkg/agentic/auto_pr_test.go index 29c004e..2884b0f 100644 --- a/pkg/agentic/auto_pr_test.go +++ b/pkg/agentic/auto_pr_test.go @@ -9,6 +9,7 @@ import ( core "dappco.re/go/core" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestAutopr_AutoCreatePR_Good(t *testing.T) { @@ -21,8 +22,8 @@ func TestAutopr_AutoCreatePR_Bad(t *testing.T) { s := &PrepSubsystem{ ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}), - backoff: make(map[string]time.Time), - failCount: make(map[string]int), + backoff: make(map[string]time.Time), + failCount: make(map[string]int), } // No status file → early return (no panic) @@ -83,8 +84,8 @@ func TestAutopr_AutoCreatePR_Ugly(t *testing.T) { s := &PrepSubsystem{ ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{}), - backoff: make(map[string]time.Time), - failCount: make(map[string]int), + backoff: make(map[string]time.Time), + failCount: make(map[string]int), } // git log origin/dev..HEAD will fail (no origin remote) → early return @@ -92,3 +93,27 @@ func TestAutopr_AutoCreatePR_Ugly(t *testing.T) { s.autoCreatePR(wsDir) }) } + +func TestAutopr_CleanupForgeBranch_Good_DeletesRemoteBranch(t *testing.T) { + remoteDir := core.JoinPath(t.TempDir(), "remote.git") + require.True(t, testCore.Process().Run(context.Background(), "git", "init", "--bare", remoteDir).OK) + + repoDir := core.JoinPath(t.TempDir(), "repo") + require.True(t, testCore.Process().Run(context.Background(), "git", "clone", remoteDir, repoDir).OK) + require.True(t, testCore.Process().RunIn(context.Background(), repoDir, "git", "config", "user.name", "Test").OK) + require.True(t, testCore.Process().RunIn(context.Background(), repoDir, "git", "config", "user.email", "test@example.com").OK) + + branch := "agent/fix-branch" + require.True(t, testCore.Process().RunIn(context.Background(), repoDir, "git", "checkout", "-b", branch).OK) + fs.Write(core.JoinPath(repoDir, "README.md"), "# test") + require.True(t, testCore.Process().RunIn(context.Background(), repoDir, "git", "add", ".").OK) + require.True(t, testCore.Process().RunIn(context.Background(), repoDir, "git", "commit", "-m", "init").OK) + require.True(t, testCore.Process().RunIn(context.Background(), repoDir, "git", "push", "-u", "origin", branch).OK) + + s := &PrepSubsystem{ServiceRuntime: core.NewServiceRuntime(testCore, AgentOptions{})} + assert.True(t, s.cleanupForgeBranch(context.Background(), repoDir, remoteDir, branch)) + + remoteHeads := testCore.Process().RunIn(context.Background(), repoDir, "git", "ls-remote", "--heads", remoteDir, branch) + require.True(t, remoteHeads.OK) + assert.NotContains(t, remoteHeads.Value.(string), branch) +} diff --git a/pkg/agentic/pr.go b/pkg/agentic/pr.go index 97afcb3..0379b03 100644 --- a/pkg/agentic/pr.go +++ b/pkg/agentic/pr.go @@ -147,6 +147,7 @@ func (s *PrepSubsystem) createPR(ctx context.Context, _ *mcp.CallToolRequest, in workspaceStatus.PRURL = pullRequestURL writeStatusResult(workspaceDir, workspaceStatus) + s.cleanupForgeBranch(ctx, repoDir, forgeRemote, workspaceStatus.Branch) if workspaceStatus.Issue > 0 { comment := core.Sprintf("Pull request created: %s", pullRequestURL) diff --git a/pkg/agentic/verify.go b/pkg/agentic/verify.go index e1bea12..ac29edd 100644 --- a/pkg/agentic/verify.go +++ b/pkg/agentic/verify.go @@ -96,6 +96,8 @@ func (s *PrepSubsystem) attemptVerifyAndMerge(repoDir, org, repo, branch string, comment := core.Sprintf("## Auto-Verified & Merged\n\n**Tests:** `%s` — PASS\n\nAuto-merged by core-agent dispatch system.", testResult.testCmd) s.commentOnIssue(context.Background(), org, repo, pullRequestNumber, comment) + forgeRemote := core.Sprintf("ssh://git@forge.lthn.ai:2223/%s/%s.git", org, repo) + s.cleanupForgeBranch(ctx, repoDir, forgeRemote, branch) return mergeSuccess }