all: s/title/slug/, adjust branch handling

There are two intertwined changes here.

First, replace title with slug, and precommit with commit-message-style.

The slug makes enough of a title, and it provides a single human-readable
identifier we can use everywhere.

Second, construct the branch name on the fly instead of storing it,
out of slug, branch prefix, and retryNumber.
This removes some duplicated data, and makes the retry loop
easier to follow and reason about.
diff --git a/loop/server/loophttp.go b/loop/server/loophttp.go
index 94d1037..5a657f8 100644
--- a/loop/server/loophttp.go
+++ b/loop/server/loophttp.go
@@ -74,7 +74,7 @@
 	MessageCount         int                           `json:"message_count"`
 	TotalUsage           *conversation.CumulativeUsage `json:"total_usage,omitempty"`
 	InitialCommit        string                        `json:"initial_commit"`
-	Title                string                        `json:"title"`
+	Slug                 string                        `json:"slug,omitempty"`
 	BranchName           string                        `json:"branch_name,omitempty"`
 	BranchPrefix         string                        `json:"branch_prefix,omitempty"`
 	Hostname             string                        `json:"hostname"`    // deprecated
@@ -1266,7 +1266,7 @@
 		WorkingDir:   getWorkingDir(),
 		// TODO: Rename this field to sketch-base?
 		InitialCommit:        s.agent.SketchGitBase(),
-		Title:                s.agent.Title(),
+		Slug:                 s.agent.Slug(),
 		BranchName:           s.agent.BranchName(),
 		BranchPrefix:         s.agent.BranchPrefix(),
 		OS:                   s.agent.OS(),
diff --git a/loop/server/loophttp_test.go b/loop/server/loophttp_test.go
index 59c5ac9..0d28fca 100644
--- a/loop/server/loophttp_test.go
+++ b/loop/server/loophttp_test.go
@@ -26,11 +26,12 @@
 	subscribers              []chan *loop.AgentMessage
 	stateTransitionListeners []chan loop.StateTransition
 	initialCommit            string
-	title                    string
 	branchName               string
 	branchPrefix             string
 	workingDir               string
 	sessionID                string
+	slug                     string
+	retryNumber              int
 }
 
 func (m *mockAgent) NewIterator(ctx context.Context, nextMessageIdx int) loop.MessageIterator {
@@ -206,12 +207,6 @@
 	return "sketch-base-test-session"
 }
 
-func (m *mockAgent) Title() string {
-	m.mu.RLock()
-	defer m.mu.RUnlock()
-	return m.title
-}
-
 func (m *mockAgent) BranchName() string {
 	m.mu.RLock()
 	defer m.mu.RUnlock()
@@ -250,6 +245,18 @@
 func (m *mockAgent) FirstMessageIndex() int                     { return 0 }
 func (m *mockAgent) DetectGitChanges(ctx context.Context) error { return nil }
 
+func (m *mockAgent) Slug() string {
+	m.mu.RLock()
+	defer m.mu.RUnlock()
+	return m.slug
+}
+
+func (m *mockAgent) IncrementRetryNumber() {
+	m.mu.Lock()
+	defer m.mu.Unlock()
+	m.retryNumber++
+}
+
 func (m *mockAgent) GetPortMonitor() *loop.PortMonitor { return loop.NewPortMonitor() }
 
 // TestSSEStream tests the SSE stream endpoint
@@ -262,9 +269,9 @@
 		subscribers:              []chan *loop.AgentMessage{},
 		stateTransitionListeners: []chan loop.StateTransition{},
 		initialCommit:            "abcd1234",
-		title:                    "Test Title",
 		branchName:               "sketch/test-branch",
 		branchPrefix:             "sketch/",
+		slug:                     "test-slug",
 	}
 
 	// Add the initial messages before creating the server