loop: always use branch sketch-wip in innie

Modify innie sketch to always create and work on a dedicated 'sketch-wip'
branch for all git operations, pushing this branch to outie instead of
pushing HEAD directly.

Having a dedicated branch name makes it clearer how to operate
inside the container, for both humans and sketch.
It also prevents the container from pushing whatever transient
commits occur while sketch does (say) a bisection or other git work.
It should also prevent sketch from constantly spinning up new
branches as it starts new tasks in a long conversation.

I'd rather have called the branch 'sketch' instead of 'sketch-wip',
but that conflicts with all the branches called 'sketch/foo'. Alas.

This was mostly written by Josh, but I made it work whether or not
sketch-wip already exists as a branch.

Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: s4ea6db2873a60129k
diff --git a/loop/agent_git_test.go b/loop/agent_git_test.go
index ae291e0..0df795d 100644
--- a/loop/agent_git_test.go
+++ b/loop/agent_git_test.go
@@ -77,6 +77,13 @@
 		t.Fatalf("Failed to create sketch-base tag: %v", err)
 	}
 
+	// Create sketch-wip branch (simulating what happens in agent initialization)
+	cmd = exec.Command("git", "checkout", "-b", "sketch-wip")
+	cmd.Dir = tempDir
+	if err := cmd.Run(); err != nil {
+		t.Fatalf("Failed to create sketch-wip branch: %v", err)
+	}
+
 	// Make a new commit
 	if err := os.WriteFile(testFile, []byte("updated content\n"), 0o644); err != nil {
 		t.Fatalf("Failed to update file: %v", err)
@@ -265,3 +272,106 @@
 		})
 	}
 }
+
+// TestSketchBranchWorkflow tests that the sketch-wip branch is created and used for pushes
+func TestSketchBranchWorkflow(t *testing.T) {
+	// Create a temporary directory for our test git repo
+	tempDir := t.TempDir()
+
+	// Initialize a git repo in the temp directory
+	cmd := exec.Command("git", "init")
+	cmd.Dir = tempDir
+	if err := cmd.Run(); err != nil {
+		t.Fatalf("Failed to initialize git repo: %v", err)
+	}
+
+	// Configure git user for commits
+	cmd = exec.Command("git", "config", "user.name", "Test User")
+	cmd.Dir = tempDir
+	if err := cmd.Run(); err != nil {
+		t.Fatalf("Failed to configure git user name: %v", err)
+	}
+
+	cmd = exec.Command("git", "config", "user.email", "test@example.com")
+	cmd.Dir = tempDir
+	if err := cmd.Run(); err != nil {
+		t.Fatalf("Failed to configure git user email: %v", err)
+	}
+
+	// Make an initial commit
+	testFile := filepath.Join(tempDir, "test.txt")
+	if err := os.WriteFile(testFile, []byte("initial content\n"), 0o644); err != nil {
+		t.Fatalf("Failed to write file: %v", err)
+	}
+
+	cmd = exec.Command("git", "add", "test.txt")
+	cmd.Dir = tempDir
+	if err := cmd.Run(); err != nil {
+		t.Fatalf("Failed to add file: %v", err)
+	}
+
+	cmd = exec.Command("git", "commit", "-m", "Initial commit")
+	cmd.Dir = tempDir
+	if err := cmd.Run(); err != nil {
+		t.Fatalf("Failed to create initial commit: %v", err)
+	}
+
+	// Create agent with configuration that would create sketch-wip branch
+	agent := NewAgent(AgentConfig{
+		Context:   context.Background(),
+		SessionID: "test-session",
+		InDocker:  true,
+	})
+	agent.workingDir = tempDir
+
+	// Simulate the branch creation that happens in Init()
+	// when a commit is specified
+	cmd = exec.Command("git", "checkout", "-b", "sketch-wip")
+	cmd.Dir = tempDir
+	if err := cmd.Run(); err != nil {
+		t.Fatalf("Failed to create sketch-wip branch: %v", err)
+	}
+
+	// Verify that we're on the sketch-wip branch
+	cmd = exec.Command("git", "branch", "--show-current")
+	cmd.Dir = tempDir
+	out, err := cmd.Output()
+	if err != nil {
+		t.Fatalf("Failed to get current branch: %v", err)
+	}
+
+	currentBranch := strings.TrimSpace(string(out))
+	if currentBranch != "sketch-wip" {
+		t.Errorf("Expected to be on 'sketch-wip' branch, but on '%s'", currentBranch)
+	}
+
+	// Make a commit on the sketch-wip branch
+	if err := os.WriteFile(testFile, []byte("updated content\n"), 0o644); err != nil {
+		t.Fatalf("Failed to update file: %v", err)
+	}
+
+	cmd = exec.Command("git", "add", "test.txt")
+	cmd.Dir = tempDir
+	if err := cmd.Run(); err != nil {
+		t.Fatalf("Failed to add updated file: %v", err)
+	}
+
+	cmd = exec.Command("git", "commit", "-m", "Update on sketch-wip branch")
+	cmd.Dir = tempDir
+	if err := cmd.Run(); err != nil {
+		t.Fatalf("Failed to create commit on sketch-wip branch: %v", err)
+	}
+
+	// Verify that the commit exists on the sketch-wip branch
+	cmd = exec.Command("git", "log", "--oneline", "-n", "1")
+	cmd.Dir = tempDir
+	out, err = cmd.Output()
+	if err != nil {
+		t.Fatalf("Failed to get git log: %v", err)
+	}
+
+	logOutput := string(out)
+	if !strings.Contains(logOutput, "Update on sketch-wip branch") {
+		t.Errorf("Expected commit 'Update on sketch-wip branch' in log, got: %s", logOutput)
+	}
+}